What does enctype='multipart/form-data' mean?

What does enctype=’multipart/form-data’ mean?

技术背景

在进行 POST 请求时,需要对请求体中的数据进行编码。HTML 表单提供了三种编码方法:application/x-www-form-urlencoded(默认值)、multipart/form-datatext/plain。此外,曾有计划添加 application/json 编码方式,但该计划已被放弃。

实现步骤

选择合适的编码类型

  • 客户端代码编写
    • 当表单包含 <input type="file"> 元素时,使用 multipart/form-data
    • 若表单不包含文件上传元素,可使用 multipart/form-dataapplication/x-www-form-urlencoded,但 application/x-www-form-urlencoded 效率更高。
  • 服务器端代码编写:使用预写的表单处理库,如 Perl 的 CGI->param 或 PHP 的 $_POST 超全局变量,这些库会处理不同编码格式的差异,无需手动解析服务器接收到的原始输入。

生成示例

可以使用 nc -l 或 ECHO 服务器,以及浏览器或 cURL 等用户代理来生成示例。以下是具体步骤:

  1. 将表单保存到一个最小的 .html 文件中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>upload</title>
</head>
<body>
<form action="http://localhost:8000" method="post" enctype="multipart/form-data">
<p><input type="text" name="text1" value="text default">
<p><input type="text" name="text2" value="aωb">
<p><input type="file" name="file1">
<p><input type="file" name="file2">
<p><input type="file" name="file3">
<p><button type="submit">Submit</button>
</form>
</body>
</html>
  1. 创建要上传的文件:
1
2
3
echo 'Content of a.txt.' > a.txt
echo '<!DOCTYPE html><title>Content of a.html.</title>' > a.html
printf 'a\xCF\x89b' > binary
  1. 运行 ECHO 服务器:
1
while true; do printf '' | nc -l localhost 8000; done
  1. 在浏览器中打开 HTML 文件,选择文件并点击提交,然后检查终端输出。

核心代码

multipart/form-data 示例

Firefox 发送的请求示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
POST / HTTP/1.1
[[ Less interesting headers ... ]]
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150
Content-Length: 834

-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="text1"

text default
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="text2"

aωb
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain

Content of a.txt.

-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file2"; filename="a.html"
Content-Type: text/html

<!DOCTYPE html><title>Content of a.html.</title>

-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file3"; filename="binary"
Content-Type: application/octet-stream

aωb
-----------------------------735323031399963166993862150--

application/x-www-form-urlencoded 示例

enctype 改为 application/x-www-form-urlencoded 后,Firefox 发送的请求示例:

1
2
3
4
5
6
POST / HTTP/1.1
[[ Less interesting headers ... ]]
Content-Type: application/x-www-form-urlencoded
Content-Length: 51

text1=text+default&text2=a%CF%89b&file1=a.txt&file2=a.html&file3=binary

最佳实践

  • 避免使用 text/plain 编码,因为它不易被计算机可靠解析,仅适用于调试。
  • 当表单包含文件上传时,使用 multipart/form-data 编码。
  • 对于不包含文件上传的表单,优先使用 application/x-www-form-urlencoded 编码,因为它效率更高。
  • 在服务器端,使用预写的表单处理库来处理表单数据,避免手动解析原始输入。

常见问题

为什么 multipart/form-data 中的边界字符串比 Content-Type 中指定的边界多两个连字符?

这是因为标准要求边界字符串以两个连字符 -- 开头。其他连字符是浏览器实现任意边界的方式。

application/x-www-form-urlencoded 为什么不适合用于文件上传?

application/x-www-form-urlencoded 会对非打印字符进行编码,每个非打印字符会增加 3 倍的开销。文件上传通常包含大量非打印字符,使用该编码方式会导致效率低下。此外,application/x-www-form-urlencoded 只会发送文件名,而不会发送文件内容。

如何确定上传文件的 Content-Type

浏览器会自动确定上传文件的 Content-Type,具体确定方式可参考:https://stackoverflow.com/questions/1201945/how-is-mime-type-of-an-uploaded-file-determined-by-browser。

表单提交时的安全问题有哪些?

根据 RFC 7578 Section 7,表单处理软件应谨慎处理用户提供的表单数据,因为其中可能包含机密或个人识别信息。multipart/form-data 本身不提供完整性检查、保密性保证、避免用户混淆等安全功能,这些问题需要由表单填充和表单数据解释应用程序来解决。接收和处理表单的应用程序必须小心,不要将未打算发送的数据返回给请求的表单处理站点。在解释 Content-Disposition 头字段中的文件名时,要注意不要意外覆盖接收方文件空间中的文件。


What does enctype='multipart/form-data' mean?
https://119291.xyz/posts/what-does-enctype-multipart-form-data-mean/
作者
ww
发布于
2025年5月26日
许可协议