准确计算 multipart/form-data 的开销
在文件上传大小限制测试中,您是否遇到过「文件只有 9MB 却出现 413 错误」的情况?其中一个原因是 multipart/form-data 的开销。通过 HTML 表单上传文件时,HTTP 请求体不仅包含文件本身,还包含额外的元数据。本文介绍了该开销的精确计算方法和测试时的注意事项。
multipart/form-data 的结构
RFC 2046 中定义的 multipart/form-data 具有每个部分由边界字符串分隔的结构。实际的 HTTP 请求正文如下所示。
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryABC123
Content-Length: 10000xyz
------WebKitFormBoundaryABC123
Content-Disposition: form-data; name="file"; filename="test.png"
Content-Type: image/png
[ファイルのバイナリデータ]
------WebKitFormBoundaryABC123--
开销明细
典型文件上传请求的开销由以下元素组成。
| 元素 | 示例 | 字节数(参考值) |
|---|---|---|
| 起始边界 | ------WebKitFormBoundaryABC123\r\n | 约 40–80 B |
| Content-Disposition 头 | Content-Disposition: form-data; name="file"; filename="test.png"\r\n | 约 60–120 B |
| Content-Type 头 | Content-Type: image/png\r\n | 约 25–50 B |
| 空行(标头末尾) | \r\n | 2 B |
| 部分末尾的换行符 | \r\n | 2 B |
| 结束 boundary | ------WebKitFormBoundaryABC123--\r\n | 约 42–82 B |
| 总开销 | 约 200~350 B |
对于仅包含一个文件的简单表单,开销约为 200–400 字节。如果存在额外的表单字段(如文本输入),开销会相应增加,但通常保持在几百字节到几千字节之间。
为什么发生413错误
Nginx 的 client_max_body_size 限制整个请求体的大小。也就是说,配置值必须包括开销。
# 10MiB のファイルをアップロードさせたい場合
# オーバーヘッド(約1KB)を考慮して少し大きめに設定
client_max_body_size 11m; # MiB単位: 11 MiB = 11,534,336 バイト
对于PHP,需要配置两个设置:upload_max_filesize(单个文件)和post_max_size(整个POST体)。
; php.ini
upload_max_filesize = 10M ; ファイル単体の上限: 10 MiB
post_max_size = 11M ; POSTボディ全体の上限: 11 MiB(オーバーヘッド分を加算)
准确的开销测量方法
如果您想准确了解实际开销,可以使用 curl 发送请求并测量请求大小。
# ファイルのバイト数を確認
wc -c test-10mb.png
# → 10485760 test-10mb.png
# curl でアップロードしてリクエストサイズを確認
curl -X POST https://example.com/upload \
-F "file=@test-10mb.png" \
-w "リクエストボディサイズ: %{size_upload} バイト\n" \
-o /dev/null -s
# → リクエストボディサイズ: 10486062 バイト(差: 302バイト)
与 Base64 编码的区别
使用 multipart/form-data,二进制文件数据按原样发送(不进行 Base64 编码)。Base64 编码仅在使用 application/x-www-form-urlencoded 发送二进制或在 JSON 主体中发送文件(如 data:image/png;base64,...)时才需要。
| 传输方式 | 开销 | 用途 |
|---|---|---|
| multipart/form-data(HTML 表单) | 数百 B ~ 数 KB | 标准文件上传 |
| JSON + Base64 | 约增加 33% | 通过 API 传输文件 |
| application/octet-stream(PUT) | 接近零 | S3 预签名 URL 等 |
测试要点
- 了解服务器的大小限制检查是针对"单个文件"还是"整个请求体"
- 由于Nginx在PHP前段限制请求,也要检查Nginx的
client_max_body_size - 多个文件的同时上传会增加开销
- 开销中也包括额外的表单字段(姓名、评论等)
使用 DevLab 的 阈值测试文件,您可以验证接近边界的文件大小的实际上传行为。请与 Nginx/Apache/PHP 配置一起验证。
总结
multipart/form-data的开销约为 200–400 字节 (单个文件的情况下)- 由于 Nginx 的
client_max_body_size限制整个请求体,设置它应稍大于文件大小限制 - PHP 需要设置两个指令:
upload_max_filesize(单个文件)和post_max_size(整个 POST 正文)。 - 通过 JSON + Base64 发送时,文件大小增加约 33%