
🐘 理想中的上传:扔进去就行
在产品经理和用户的眼里,文件上传就是把本地的文件“复制粘贴”到服务器上:
| 前端选择 | 1 行 | <input type="file"> |
| 发起上传 | 1 行 | FormData.append('file', file); axios.post(url, data) |
| 后端接收 | 1 行 | file.saveTo(disk) |
总计:3 行。
如果文件只有 1MB(比如头像),这确实是真的。
但当老板说:“我们要支持 2GB 的视频上传,还要支持断网续传,还要支持秒传”时,这 3 行代码就变成了一个庞大的分布式文件处理系统。
💥 第一关:大文件的“暴食症” (OOM & Timeout)
如果你敢直接把一个 2GB 的文件塞进 HTTP POST 请求里发给后端:
结论: 大文件不能“吞”,只能“切”。
🔪 第二关:千刀万剐 (分片上传 Chunking)
为了解决“太大”的问题,程序员必须化身为外科医生,把文件切成无数个小块。
前端逻辑:
利用 Blob.slice() 方法,把 2GB 的文件切成 2000 个 1MB 的小切片。
然后起一个循环,发 2000 个 HTTP 请求:
- “这是第 1 片”
- “这是第 2 片”
- …
后端逻辑:
后端变成了收废品站。
我不存完整文件了,我先在一个临时目录里堆放这 2000 个碎片文件:file_abc_part_1, file_abc_part_2…
代码山的代价:
- 你要管理切片顺序。
- 你要处理并发上传(浏览器限制同域名只能有 6 个 TCP 连接,你得写个队列)。
- 你要处理失败重试(第 500 片传失败了,只能重传这一片,不能重传整个文件)。
🧩 第三关:拼图地狱 (Merge)
所有切片都传完了,文件还在服务器的临时目录里躺着,是散的。
前端发来一个指令:“传完了,合并吧!”
后端噩梦开始:
⏸️ 第四关:断点续传的“记忆术”
用户传到 50%(第 1000 片),网断了。
半小时后,网好了。用户重新选择了文件。
如果从头传,用户会杀人。
系统必须有记忆。
交互流程:
坑点:
前端算 MD5 很慢!
让浏览器去读 2GB 文件算哈希值,页面会卡死。
优化技巧: 抽样计算(只算文件头、文件尾和中间几块),牺牲一点准确性换取速度。
⚡ 第五关:秒传 (Instant Upload) 的魔术
这是让用户觉得“哇,你们网速好快”的诈骗功能。
场景:
用户 A 上传了一部电影《复仇者联盟》,传了 1 小时。服务器上有了这份文件。
用户 B 也上传同一部电影。
前端一算 MD5,发给后端。
后端一看:“嘿!这文件我库里有啊!”
后端直接返回:“上传成功!”(耗时 0.1 秒)。
实际上: 用户 B 的文件根本没上传,后端只是在数据库里给用户 B 的文件列表里,加了一条引用 (Link),指向了用户 A 传的那份文件。
风险:
哈希碰撞 (Hash Collision)。
虽然概率极低,但理论上可能存在两个不同的文件 MD5 一样。
如果发生了,用户 B 上传了自己的自拍,结果下载下来变成了《复仇者联盟》。
为了防这个,通常要用 MD5 + SHA1 双重校验,或者对比文件长度。
🧹 第六关:垃圾清理 (GC)
场景:
每天有 1000 个人上传文件。
其中 100 个人传了一半就走了,再也没回来。
服务器的临时目录里留下了几十万个永远不会被合并的碎片文件。
随着时间推移,磁盘爆满。
防御代码:
你必须写一个定时任务(Cron Job),每天凌晨爬起来巡逻:
“这些碎片是谁的?都三天了还没合并?删了!”
💡 总结:这不是上传,这是分布式工程
现在你明白了,那个简单的 <input type="file"> 背后,其实跑着一个复杂的分布式系统:
所以,当下一次你看到进度条卡在 99% 时,请不要怪网速。
那通常是后端正在拼命地把几千个碎片往一起缝合,CPU 正在冒烟,磁盘正在尖叫。
网硕互联帮助中心





评论前必须登录!
注册