它如何在内部发送文件?
该格式被称为multipart/form-data,就像在问:enctype='multipart/form-data'是什么意思?
我将:
添加更多HTML5参考
用一个表单提交的例子解释为什么他是对的
HTML5的引用
enctype有三种可能:
x-www-urlencoded
multipart/form-data(规范指向RFC2388)
文字保持朴实的风格。这是“计算机无法可靠地解释的”,所以它永远不应该用于生产,我们也不会进一步研究它。
如何生成示例
一旦您看到每种方法的示例,您就会清楚地知道它们是如何工作的,以及应该在什么时候使用每种方法。
你可以使用以下方法生成例子:
nc -l或ECHO服务器:接受GET/POST请求的HTTP测试服务器
像浏览器或cURL这样的用户代理
将表单保存为最小的.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ω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>
我们将默认文本值设置为aωb,这意味着aωb,因为ω是U+03C9,这是UTF-8中的字节61 CF 89 62。
创建文件上传:
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
运行我们的echo服务器:
while true; do printf '' | nc -l 8000 localhost; 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 (aωb在UTF-8中)按字面形式发送。你可以用nc -l localhost 8000 | hd来验证,它表示字节:
61 CF 89 62
发送了(61 == 'a'和62 == 'b')。
因此,很明显:
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150 sets the content type to multipart/form-data and says that the fields are separated by the given boundary string.
But note that the:
boundary=---------------------------735323031399963166993862150
has two less dadhes -- than the actual barrier
-----------------------------735323031399963166993862150
This is because the standard requires the boundary to start with two dashes --. The other dashes appear to be just how Firefox chose to implement the arbitrary boundary. RFC 7578 clearly mentions that those two leading dashes -- are required:
4.1. multipart/form-data的“Boundary”参数
与其他多部分类型一样,各部分用
边界分隔符,使用CRLF、“——”和值构造
“boundary”参数。
every field gets some sub headers before its data: Content-Disposition: form-data;, the field name, the filename, followed by the data.
The server reads the data until the next boundary string. The browser must choose a boundary that will not appear in any of the fields, so this is why the boundary may vary between requests.
Because we have the unique boundary, no encoding of the data is necessary: binary data is sent as is.
TODO: what is the optimal boundary size (log(N) I bet), and name / running time of the algorithm that finds it? Asked at: https://cs.stackexchange.com/questions/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequences
Content-Type is automatically determined by the browser.
How it is determined exactly was asked at: How is mime type of an uploaded file determined by browser?
应用程序/ x-www-form-urlencoded
现在将加密类型更改为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
显然,文件数据没有被发送,只发送了基本名称。所以这不能用于文件。
至于文本字段,我们看到通常可打印的字符(如a和b)在一个字节内发送,而不可打印的字符(如0xCF和0x89)则各占用3个字节:%CF%89!
比较
上传的文件通常包含大量不可打印的字符(例如图像),而文本表单几乎没有。
从例子中我们看到:
Multipart /form-data:在消息中增加了一些字节的边界开销,并且必须花一些时间计算它,但是每个字节都是一个字节发送的。
Application /x-www-form-urlencoded:每个字段有一个单字节边界(&),但是为每个不可打印字符增加了3倍的线性开销系数。
因此,即使我们可以发送带有application/x-www-form-urlencoded的文件,我们也不想这样做,因为这样效率很低。
但是对于在文本字段中找到的可打印字符,这并不重要,而且产生的开销更少,所以我们只使用它。