enctype='multipart/form-data' 是什么意思?

新手上路,请多包涵

enctype='multipart/form-data' 在 HTML 表单中是什么意思,我们什么时候应该使用它?

原文由 EBAG 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 785
2 个回答

当您发出 POST 请求时,您必须以某种方式对构成请求主体的数据进行编码。

HTML 表单提供 三种编码方法

  • application/x-www-form-urlencoded (默认)
  • multipart/form-data
  • text/plain

正在进行添加 application/json 的工作,但已被放弃。

(对于使用 HTML 表单提交以外的其他方式生成的 HTTP 请求,其他编码是可能的。JSON 是用于 Web 服务的常见格式,有些仍然使用 SOAP。)

对于大多数开发人员而言,格式的细节并不重要。要点是:

  • 切勿使用 text/plain

当您编写客户端代码时:

  • 使用 multipart/form-data 当您的表单包含任何 <input type="file"> 元素时
  • 否则你可以使用 multipart/form-dataapplication/x-www-form-urlencoded 但是 application/x-www-form-urlencoded 会更有效率

当您编写服务器端代码时:

  • 使用预先编写的表单处理库

大多数(例如 Perl 的 CGI->param 或 PHP 的 $_POST superglobal)会为你处理这些差异。不要试图解析服务器接收到的原始输入。

有时您会发现一个库无法处理这两种格式。 Node.js 最流行的处理表单数据的库是 body-parser ,它不能处理多部分请求(但有文档推荐了一些可以处理的替代方法)。


如果您正在编写(或调试)用于解析或生成原始数据的库,那么您需要开始担心格式。为了兴趣,您可能还想了解它。

application/x-www-form-urlencoded 与 URL 末尾的查询字符串大致相同。

multipart/form-data 要复杂得多,但它允许将整个文件包含在数据中。在 HTML 4 规范 中可以找到结果的示例。

text/plain 由 HTML 5 引入,仅对调试有用——来自 规范: _它们不能被计算机可靠地解释_——我认为其他的与工具结合(比如开发人员中 的网络面板 大多数浏览器的工具)对此更好)。

原文由 Quentin 发布,翻译遵循 CC BY-SA 4.0 许可协议

我们应该什么时候使用它?

Quentin 的回答 是正确的:使用 multipart/form-data 如果表单包含文件上传,则使用 application/x-www-form-urlencoded 否则,如果您省略 enctype ,则使用默认值

我要去:

  • 添加更多 HTML5 参考
  • 用表单提交示例解释 为什么 他是对的

HTML5 参考资料

enctype 存在 三种可能性

如何生成示例

看到每种方法的示例后,它们的工作原理以及应在何时使用每种方法就会一目了然。

您可以使用以下方法生成示例:

将表格保存到最小的 .html 文件:

 <!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&#x03C9;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>

We set the default text value to a&#x03C9;b , which means aωb because ω is U+03C9 , which are the bytes 61 CF 89 62 在 UTF-8 中。

创建要上传的文件:

 echo 'Content of a.txt.' > a.txt

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

# Binary file containing 4 bytes: 'a', 1, 2 and 'b'.
printf 'a\xCF\x89b' > binary

运行我们的小回声服务器:

 while true; do printf '' | nc -l localhost 8000; done

在浏览器上打开 HTML,选择文件并单击提交并检查终端。

nc 打印收到的请求。

测试于:Ubuntu 14.04.3, nc BSD 1.105,Firefox 40。

多部分/表单数据

火狐发送:

 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--

对于二进制文件和文本字段,字节 61 CF 89 62 (UTF-8 中的 aωb )按字面意思发送。您可以使用 nc -l localhost 8000 | hd 来验证,它表示字节:

 61 CF 89 62

已发送( 61 == ‘a’ 和 62 == ‘b’)。

因此很明显:

  • Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150 将内容类型设置为 multipart/form-data 并表示字段由给定的 boundary 字符串分隔。

但请注意:

   boundary=---------------------------735323031399963166993862150

比实际屏障少两个破折号 --

   -----------------------------735323031399963166993862150

这是因为标准要求边界以两个破折号 -- 。其他破折号似乎正是 Firefox 选择实现任意边界的方式。 RFC 7578 清楚地提到 这两个前导破折号 -- 是必需的:

4.1. multipart/form-data 的“边界”参数

与其他多部分类型一样,各部分由边界定界符分隔,使用 CRLF、“–”和“边界”参数的值构造。

  • 每个字段在其数据之前都有一些子标题: Content-Disposition: form-data; ,字段 namefilename ,然后是数据。

服务器读取数据直到下一个边界字符串。浏览器必须选择不会出现在任何字段中的边界,因此这就是边界可能因请求而异的原因。

因为我们有唯一的边界,所以不需要对数据进行编码:二进制数据按原样发送。

TODO:什么是最佳边界大小( log(N) 我打赌),以及找到它的算法的名称/运行时间?问: https ://cs.stackexchange.com/questions/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequences

  • Content-Type 由浏览器自动确定。

在以下位置询问了如何准确确定: How is mime type of an uploaded file determined by browser?

应用程序/x-www-form-urlencoded

现在将 enctype 更改为 application/x-www-form-urlencoded ,重新加载浏览器,然后重新提交。

火狐发送:

 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

显然没有发送文件数据,只有基本名称。所以这不能用于文件。

至于文本字段,我们看到常用的可打印字符,如 ab 以一个字节发送,而不可打印的字符,如 —225915b5b3dea7f1d1abfc1b2-da 0x89 0xCF --- 每个占用 3个字节%CF%89

比较

文件上传通常包含大量不可打印的字符(例如图像),而文本形式几乎从不包含。

从例子中我们看到:

  • multipart/form-data : 给消息增加了几个字节的边界开销,并且必须花一些时间来计算它,但是一个字节发送一个字节。

  • application/x-www-form-urlencoded :每个字段有一个字节边界( & ),但为每个不可打印字符添加 3x线性 开销因子。

因此,即使我们可以使用 application/x-www-form-urlencoded 发送文件,我们也不想这样做,因为它效率太低了。

但对于在文本字段中找到的可打印字符,它并不重要并且产生的开销较小,因此我们只使用它。

原文由 Ciro Santilli OurBigBook.com 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题