你有没有试过备份一个20GB的视频项目,传到一半突然断网,再点开始——结果又从头跑?气得想砸硬盘。其实这问题早有解法:断点续传。它不是什么黑科技,而是靠几个关键动作配合完成的‘接力跑’。
核心就三件事
第一,客户端得记住‘跑到哪了’;第二,服务端得能按位置接上;第三,两边得对得上号,别A说传了1.3GB,B以为才800MB。
常见做法是把文件切成块(比如每块512KB),上传前先发个请求问服务器:‘我这个文件的MD5是abc123,目前已传完哪些块?’服务器查数据库或临时文件索引,返回[0,1,3,4]——意思第0、1、3、4块已存好,第2块缺着。客户端立刻跳过前四块,从第2块开始续传。
HTTP Range头是基础功
真正在传输层起作用的是HTTP协议里的Range和Content-Range。上传时用PUT带Range: bytes=1048576-2097151,表示这次只传第2块(从1MB到2MB);服务端收到后,用206 Partial Content响应确认接收成功,同时写入对应偏移位置。
代码里长啥样?(简化版逻辑)
const uploadChunk = async (file, start, end, uploadId) => {
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('upload_id', uploadId);
formData.append('offset', start);
await fetch('/api/upload/chunk', {
method: 'POST',
body: formData
});
};
// 恢复上传时先查已传范围
const getUploadedRanges = async (fileHash) => {
const res = await fetch(`/api/upload/status?hash=${fileHash}`);
return res.json(); // e.g. { uploaded: [0, 512000, 1024000] }
};
光有Range还不够
真实备份场景中,还得防意外:手机切后台被系统杀掉、笔记本合盖休眠、NAS中途重启……所以客户端本地要持久化记录,比如用IndexedDB存当前文件的upload_id、last_offset、file_hash。下次打开App,先捞出这些信息,再去服务器校验一致性,而不是盲目重传。
另外,合并阶段不能简单cat chunk_0 chunk_1 > final.file。得校验每块SHA256,最后再算整文件哈希,确保没丢字节、没串块——否则备份出来的文件,看着大,一打开就报错。
举个身边例子
上周帮朋友恢复误删的婚礼录像,原始素材在旧Mac上,要传到新NAS。用rsync加--partial --progress参数,断了三次电,最后还是完整同步成功。它底层原理类似:比对文件块指纹,只传差异+未完成部分。你用的备份工具,只要支持‘暂停/继续’,背后大概率就在跑这一套。