分片上传必须绕过原生表单,用javascript手动切片并发送:监听file输入事件,用file.slice()分块,每块封装formdata附带uploadid(每文件独立生成)和从0开始的chunkindex,通过fetch发送;html只需最小结构且避免form包裹。

分片上传不能直接用 <form></form> 提交
原生 <form></form> 只能整文件提交,不支持切片、暂停、续传或并发控制。强行把分片塞进表单会破坏 enctype="multipart/form-data" 的语义——浏览器按整个文件打包,后端收到的不是“第3片”,而是“一个叫 chunk_3 的普通字段”,无法还原原始二进制流。
真实做法是绕过表单提交,用 JavaScript 手动构造请求:
- 监听
<input type="file">的change事件,拿到File对象 - 用
File.slice()按固定chunkSize切出Blob片段 - 每个片段单独封装成
FormData,显式 appendfile字段和业务参数(如uploadId、chunkIndex) - 用
fetch()或XMLHttpRequest发送,避免表单重定向和页面刷新
uploadId 和 chunkIndex 必须在前端生成并严格对齐
服务端靠这两个字段识别归属与顺序。错一帧,拼出来的文件就损坏。
常见错误:
- 用
Date.now()或文件名生成uploadId→ 并发上传同名文件时冲突 -
chunkIndex从 1 开始但后端按 0 起始合并 → 缺失第 0 片,首块数据丢失 - 用
Math.floor(file.size / chunkSize)算totalChunks→ 浮点误差导致最后一片被截断
正确写法:
const uploadId = crypto.randomUUID(); // 或 uuidv4() const chunkSize = 5 * 1024 * 1024; for (let i = 0; i * chunkSize <h3>HTML 结构只需承载选择与触发,别掺和逻辑</h3><p>表单标签本身不参与分片逻辑,它的作用仅限于:提供文件选择入口、绑定事件、展示状态。过度嵌套或加多余属性反而干扰 JS 控制。</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill6712" title="Wechat HTML Publisher"><img src="https://img.php.cn/upload/skill/000/000/081/179109368394970.jpg" alt="Wechat HTML Publisher" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill6712" title="Wechat HTML Publisher" class="overflowclass">Wechat HTML Publisher</a> <p class="overflowclass">直接上传HTML富文本到微信公众号草稿箱。支持完整的HTML格式,无需Markdown转换。</p> </div> <a rel="nofollow" href="/xiazai/skill6712" title="Wechat HTML Publisher" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div><p>推荐最小结构:</p><pre class="brush:php;toolbar:false;"><input type="file" id="fileInput" accept="video/*" multiple><button id="uploadBtn">开始上传</button> <div id="progress"></div>
注意点:
- 不要给
<input>设name属性——你不用它走表单提交 - 不要包裹在
<form></form>里,除非你同时保留整文件回退方案 -
accept属性可辅助前端过滤,但不可替代后端校验
多个文件共存时,uploadId 必须 per-file 隔离
用户一次拖入 3 个视频,就得生成 3 个独立 uploadId,每个对应自己的 chunkIndex 序列(0→N₁, 0→N₂, 0→N₃)。复用同一个 uploadId 会导致所有文件的分片混进同一队列,后端无法区分哪片属于哪个文件。
实现上,应在 handleFileSelect 中为每个 File 实例初始化独立上下文:
const files = Array.from(e.target.files);
files.forEach(file => {
const fileId = crypto.randomUUID();
const totalChunks = Math.ceil(file.size / chunkSize);
// 后续每个分片请求都带 { uploadId: fileId, chunkIndex: i }
});
真正容易被忽略的是:上传中途用户切换文件或刷新页面,uploadId 若未持久化到 localStorage,断点续传就失效——这个 ID 是客户端状态锚点,不是一次性的临时值。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










