跳到内容

准确计算 multipart/form-data 的开销

分类:HTTP·通信

在文件上传大小限制测试中,您是否遇到过「文件只有 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\n2 B
部分末尾的换行符\r\n2 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%

❓ 常见问题

multipart/form-data 的开销有多大?
只有一个文件的简单表单,开销约 200~400 字节,由 boundary 字符串、Content-Disposition 头、Content-Type 头及换行符构成。若有额外表单字段还会增加,但通常在数百字节到数 KB 之间。
文件未超上限为何仍出现 413 错误?
Nginx 的 client_max_body_size 限制的是整个请求体。当 multipart 开销(boundary、请求头等)使总量超过设定值时就会返回 413。应把 client_max_body_size 设得比预期的文件上限稍大一些。
multipart/form-data 与 Base64 编码哪个开销更大?
Base64(经由 JSON 发送)开销大得多,会使文件体积增加约 33%;而 multipart/form-data 原样发送二进制数据,开销仅数百字节到数 KB。