大文件分片上传与断点续传
❓ 面试官:如果用户要上传一个 5GB 的高清视频文件,直接用传统方式上传肯定会超时或者内存溢出。你怎么设计这个上传架构?如何实现断点续传?【中高级】
频率:🔥🔥🔥🔥
💡 一句话总结(先抛结论): 核心思想是化整为零。不能把 5G 的文件作为一个整体在网络上飘。必须在前端利用浏览器的 File API 把文件切分成无数个小小的“分片(Chunk)”,再并发上传。后端接收完所有分片后,再进行“合并(Merge)”。
📝 详细设计方案与流程剧本:
第一步:文件秒传(极客体验的加分项)
- 原理:很多时候用户传的文件服务器上早就有了(比如某部热门电影)。
- 做法:在前端选好文件后,利用 JS 库(如
spark-md5)计算出整个文件的 MD5 的 Hash 值。把它当作文件的唯一“指纹”。 - 在正式上传前,前端先发一个请求:
校验文件接口(md5)。后端去数据库一查,如果这 MD5 已经存在于对象存储(OSS)中,直接返回“该文件已存在”,前端页面瞬间显示 100% 上传完成。这就是百度网盘里经常遇到的“秒传”原理。
第二步:文件分片与并发上传
- 如果文件没有被秒传。前端设定一个分片大小(比如 5MB)。
- 5GB 的文件会被切分成 1000 个分片。前端给每个分片编上号(chunkIndex: 0~999),连同刚才的整体 md5 值,并发地发给后端。
- 后端写一个上传接口,接收
(文件流, chunkIndex, md5)。收到分片后,把它暂时存到一个临时目录里,文件名命名为md5_chunkIndex(比如abcd123_0)。
第三步:断点续传机制
- 痛点:传到 800 个分片时网断了,下次重传要从头开始吗?
- 解决方案:不需要!网络恢复后,前端再次上传前,先调一个后端的接口:
查询已上传分片(md5)。 - 后端去临时目录里一扫,发现这个 md5 对应的文件已经有 0~799 号分片了,把这个数组
[0,1,...,799]返回给前端。 - 前端拿到后一对比,聪明地跳过这些分片,只把剩下的 800~999 号分片发过去。这就实现了断点续传。
第四步:文件合并(终局操作)
- 当所有的 1000 个分片都传完后,前端发一个特殊的请求:
合并文件(md5, 总分片数)。 - 后端收到指令,去临时目录下找到那 1000 个碎文件,按序号从小到大排好,用输入输出流(
RandomAccessFile或FileChannel)把它们按顺序追加拼接成一个完整的大文件。 - 最后,再计算一次这个合成大文件的 MD5,和前端最初传过来的 MD5 进行对比。如果一致,说明文件在上传过程中一字节都没坏。
- 最终把大文件丢进阿里云 OSS 或者分布式文件系统(MinIO)里,把临时碎文件删除,完成整个闭环!
🌟 性能优化细节(高阶加分项): "面试官,如果在单机服务器上做合并,5G 的文件读写极其吃磁盘 IO。在真实的生产环境(比如使用阿里云 OSS 时),我们后端其实连合并这一步都不需要做。 阿里云 OSS 原生支持分片上传机制。前端把分片直接通过直传 Token 传给阿里的服务器,最后前端调阿里云的 CompleteMultipartUpload 接口,阿里内部自己就帮你合并了。后端根本不接触庞大的文件流,既省了带宽又省了磁盘资源。"