File.slice() 参数是字节偏移量而非字符或行数,须用 file.size 边界校验;需兼容旧浏览器前缀;并发上传须限流(如3–5路);断点续传依赖文件指纹而非文件名;重试应精准到失败分片并携带chunkIndex、totalChunks、chunkHash。

File.slice() 切片时 start 和 end 必须是字节偏移量
很多人误用 file.slice(0, 100) 想切前 100 个“字符”或“行”,结果上传失败或内容错乱。File 是二进制 Blob,slice() 的参数单位是字节(byte),不是字符数、行数,也不是 MB 字符串。比如 2MB 分片要写成 file.slice(0, 2 * 1024 * 1024),不能写 file.slice(0, '2MB') 或 file.slice(0, 2000000)(虽数值对但可读性差,易出错)。
常见错误现象:上传后文件损坏、服务端校验哈希不一致、Content-Range 头报错 Invalid range。
- 始终用
file.size做边界判断,end = Math.min(start + chunkSize, file.size) - 不同浏览器旧版本需兼容前缀:
file.webkitSlice?.(start, end)或file.mozSlice?.(start, end),现代浏览器已统一支持原生slice - 切片返回的是新
Blob,不是引用,原file不受影响
并发上传必须控制请求数量,别直接 Promise.all(fileChunks)
把 1GB 文件切成 200 个 5MB 分片,然后 Promise.all(chunks.map(upload)) 会瞬间发起 200 个 XMLHttpRequest,触发浏览器连接数限制(Chrome 默认 6 个同域并发)、服务端限流、内存暴涨甚至页面卡死。
推荐用 promise 队列或信号量控制并发,保持 3–5 路稳定上传:
- 用
async/await串行上传最简单,但慢;想提速就用Promise.allSettled+ 固定长度队列(如每次最多发 4 个) - 每个
XMLHttpRequest实例必须独立创建,不能复用;上传完立即销毁(设xhr = null) - 务必在请求头中带上
Content-Range,例如bytes 0-5242879/1073741824,服务端靠它定位分片位置
断点续传依赖文件唯一标识,不能只靠文件名
用户重选同名文件,或同一文件多次修改后上传,仅靠 file.name 无法区分。服务端若按名查进度,会误判“已传完”或覆盖旧进度。
可靠做法是前端计算文件指纹(如 SparkMD5 或 Web Crypto API 的 digest('SHA-256', buffer)),生成全局唯一 ID:
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
- 先读取整个文件的哈希(可用
FileReader分块读 + 流式计算,避免内存爆) - 将该哈希作为
uploadId发给服务端预检,服务端返回已上传的分片索引列表 - 前端跳过这些索引,只上传剩余分片;刷新页面后从
localStorage读取uploadId和进度继续 - 注意:不要在主线程同步算大文件 MD5,会阻塞 UI;要用
Web Worker或分片异步累加
上传失败后重试必须带分片序号和校验信息
网络抖动导致某一分片 408 或 502,如果只是无差别重发所有分片,既浪费带宽又延长耗时。关键是要让重试精准到失败的那一片。
每片上传应携带三项元数据:
-
chunkIndex:从 0 开始的整数,服务端靠它排序合并 -
totalChunks:总片数,服务端用于校验是否收齐 -
chunkHash(可选但推荐):该分片的 SHA-1 或 MD5,服务端收到后比对,防传输损坏
重试逻辑应封装为独立函数,接收 { chunk, index, uploadId },而非整个文件对象——否则容易重复初始化 FileReader 或触发多次 slice。
真正容易被忽略的是:服务端返回的“已存在分片”响应,前端必须解析并跳过,而不是当成错误抛出;还有,XMLHttpRequest.abort() 后不能立刻 reuse 同一个实例,必须新建。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










