multipart/form-data通过边界分隔符将文件与文本字段封装为独立part,每个part含content-disposition和原始二进制数据,浏览器自动生成boundary并构造完整请求体。

form enctype="multipart/form-data" 是怎么把文件塞进 HTTP 请求体的
浏览器用 multipart/form-data 上传文件时,并不是直接把整个文件二进制流“贴”进请求体,而是按 RFC 7578 规范构造一个多部件(multipart)消息体。每个表单项(包括普通字段和文件)都被包装成一个独立“part”,用随机生成的 boundary 字符串分隔。
关键点在于:文件内容本身不编码,但每个 part 的头部必须带 Content-Disposition,例如:
Content-Disposition: form-data; name="uploaded_file"; filename="report.pdf" Content-Type: application/pdf <binary data here></binary>
服务端收到后,需按 boundary 解析出各个 part —— 这就是 PHP 的 $_FILES 数组能自动提取 tmp_name、name、size 等字段的根本原因。但这个机制天然不支持大文件:HTTP 请求是一次性发完的,没有“暂停”“续传”“校验偏移”概念。
常见错误现象:
- 上传 200MB 文件时,Nginx 报
413 Request Entity Too Large,其实是client_max_body_size拦截了整条 multipart 请求 - PHP 返回
UPLOAD_ERR_INI_SIZE(错误码 1),对应upload_max_filesize或post_max_size超限 - 用户刷新页面后,上传进度归零,服务端无任何中间状态记录
分片上传必须绕开 form 表单提交机制
一旦决定做分片,就彻底放弃 <form enctype="multipart/form-data"></form> 提交方式。它和分片在协议层是互斥的:表单提交是原子操作;分片上传是多次独立 HTTP 请求,每次只传一段字节范围 + 元数据。
实操中必须用 XMLHttpRequest 或 fetch 手动构造请求,核心要素包括:
- 每个分片用
File.prototype.slice()截取,例如file.slice(start, end) - 每个请求 body 使用
FormData封装,但 key 名不能叫uploaded_file这类通用名,而应含唯一标识,如file_id、chunk_index、total_chunks - 必须显式设置
Content-Type: multipart/form-data; boundary=...—— 但现代浏览器调用new FormData()后发请求时会自动处理 boundary,你不用手动拼 - 服务端不能依赖
$_FILES的默认行为,要从$_POST里读元数据,再从php://input或临时$_FILES中取当前分片二进制
容易踩的坑:用 fetch 传 FormData 时没删掉 Content-Type 头,导致浏览器自动加的 boundary 被覆盖,服务端解析失败。
分片上传的最小必要元数据字段设计
前端每次发分片,至少得告诉后端四件事,缺一不可:
-
file_id:客户端生成的唯一哈希(如md5(file.name + file.size + Date.now())),用于合并时识别同一文件 -
chunk_index:从 0 开始的整数,表示这是第几片 -
total_chunks:总片数,让服务端知道是否收齐 -
chunk_hash(可选但强烈建议):当前分片的 SHA-256,用于校验传输完整性,防网络丢包错位
为什么不用 filename 做主键?因为用户可能上传同名文件,或重命名后再传,仅靠名字无法区分不同上传会话。为什么 chunk_index 从 0 开始?因为 File.slice(0, size) 更符合 JS 数组习惯,也方便后端用数组索引存临时分片。
性能影响:如果每片都带完整 filename 和 file_type,会增加请求体积;实际只需首次上传时传一次,后续分片只传 file_id + chunk_index 即可。
断点续传依赖服务端持久化分片状态
浏览器关掉再打开,能“接着传”,靠的不是前端记住了什么,而是服务端存了哪些分片已接收。这个状态不能只放内存,必须落盘或进数据库。
最简方案是用文件系统模拟:
- 上传前,前端先 GET
/api/chunks?file_id=abc123,服务端查uploads/abc123/目录下已有多少chunk_*.bin文件,返回已传的chunk_index列表 - 前端跳过这些 index,只传缺失的分片
- 所有分片收齐后,服务端执行合并:
cat uploads/abc123/chunk_*.bin > uploads/abc123/final.file
容易被忽略的地方:合并操作必须加锁,否则并发上传同一 file_id 时可能产生竞态;chunk_*.bin 文件权限需设为 600,防止未授权访问原始分片;临时目录磁盘空间必须监控,避免占满。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











