HTML5本身不支持断点续传,需前端用Blob.slice()切片、服务端记录已传分片并返回uploaded列表,前端据此跳过已传块;关键在uploadId基于文件内容哈希、禁用缓存、缓存File实例。

HTML5 中没有现成的 uploadWithResume API
浏览器原生不提供断点续传上传接口,XMLHttpRequest 和 fetch 都只负责单次请求。所谓“断点续传”,本质是前端主动切片 + 服务端配合记录 + 前后端约定校验逻辑。关键不在 HTML5 本身,而在如何用 Blob.slice()、File 对象和 Range 请求头协同工作。
核心三步:切片、校验、拼接
断点续传不是“暂停再继续”,而是把大文件拆成固定大小的 Blob,每次传一个块,并让服务端返回已接收的块列表,前端跳过已传部分。常见错误是直接对整个 File 调用 send(),结果失败就全重传。
- 用
file.slice(start, end)切出每个分片(注意:slice返回新Blob,不是字符串截取) - 每个分片请求必须带唯一标识:如
uploadId(由首次请求创建)、chunkIndex、totalChunks、chunkHash(可选,防重复) - 服务端需保存每个
uploadId下已成功接收的chunkIndex集合,响应时返回{ uploaded: [0,2,4] } - 前端根据响应决定下一次从哪个
chunkIndex开始传,而非简单 +1
fetch 传分片时必须处理 409 Conflict 和 208 Already Reported
很多实现忽略服务端对重复分片的响应码。当网络抖动导致分片重发,服务端应返回 409(已存在)或 208(避免前端误判为失败),前端收到后应直接跳过该块,继续下一个。否则会卡死或覆盖写错位置。
fetch('/upload', {
method: 'POST',
headers: { 'X-Upload-ID': uploadId, 'X-Chunk-Index': '5' },
body: blobSlice
})
.then(res => {
if (res.status === 208 || res.status === 409) return { skip: true };
return res.json();
});
真正容易被绕过的细节:时间戳、缓存与 Blob 引用
开发中常因以下问题导致“看似续传,实则重头来”:
立即学习“前端免费学习笔记(深入)”;
-
File.lastModified或File.name变了,服务端认为是新文件,清空历史记录 → 必须用文件内容哈希(如spark-md5前 2MB)作为uploadId基础 - Chrome 对相同 URL 的
fetch可能复用缓存响应 → 所有上传请求 URL 后加随机参数或禁用缓存:headers: { 'Cache-Control': 'no-cache' } - 切片后未保留原始
File引用,用户切换页面再回来时file.slice()报错 → 必须在首次选择文件时缓存file实例,不要依赖 input 元素的files[0]后续读取











