当我提交一个附加文件的简单表格时:
<form enctype="multipart/form-data" action="http://localhost:3000/upload?upload_progress_id=12344" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="100000" />
Choose a file to upload: <input name="uploadedfile" type="file" />
<input type="submit" value="Upload File" />
</form>
它是如何在内部发送文件的?该文件是作为数据发送的HTTP主体的一部分吗?在此请求的标头中,我没有看到任何与文件名相关的内容 .
我只是想知道发送文件时HTTP的内部工作原理 .
5 回答
让我们来看看当您选择文件并提交表单时会发生什么(为简洁起见我截断了 Headers ):
表单参数(包括文件数据)不是URL编码表单参数,而是作为请求正文中的多部分文档中的部分发送 .
在上面的示例中,您可以看到输入
MAX_FILE_SIZE
,其中包含表单中设置的值,以及包含文件数据的部分 . 文件名是Content-Disposition
标头的一部分 .完整的详细信息是here .
该格式称为multipart/form-data,如下所示:What does enctype='multipart/form-data' mean?
我要去:
添加更多HTML5参考
解释 why 他是一个表单提交示例
HTML5参考
enctype
为enctype
:x-www-urlencoded
multipart/form-data(规格指向RFC2388)
text-plain . 这是"not reliably interpretable by computer",所以它永远不应该在 生产环境 中使用,我们不会进一步研究它 .
如何生成示例
一旦你看到每个方法的一个例子,就会明白它们是如何工作的,以及何时应该使用每个方法 .
您可以使用以下方法生成示
nc -l
或ECHO服务器:HTTP test server accepting GET/POST requests用户代理,如浏览器或cURL
将表单保存到最小的
.html
文件:我们将默认文本值设置为
aωb
,这意味着aωb
因为ω
是U+03C9
,这是UTF-8中的字节61 CF 89 62
.创建要上传的文件:
运行我们的小型echo服务器:
在浏览器上打开HTML,选择文件,然后单击“提交”并检查终端 .
nc
打印收到的请求 .测试:Ubuntu 14.04.3,
nc
BSD 1.105,Firefox 40 .multipart / form-data
Firefox发送:
对于二进制文件和文本字段,按字面意思发送字节
61 CF 89 62
(UTF-8中的aωb
) . 您可以使用nc -l localhost 8000 | hd
来验证它,它表示字节:已发送(
61
== 'a'和62
== 'b') .因此很明显:
Content-Type: multipart/form-data; boundary=---------------------------9051914041544843365972754266
将内容类型设置为multipart/form-data
,并表示字段由给定的boundary
字符串分隔 .每个字段在其数据之前获得一些子 Headers :
Content-Disposition: form-data;
,字段name
,filename
,后跟数据 .服务器读取数据直到下一个边界字符串 . 浏览器必须选择不会出现在任何字段中的边界,因此这就是边界可能因请求而异的原因 .
因为我们有唯一的边界,所以不需要对数据进行编码:二进制数据按原样发送 .
TODO:最佳边界大小是什么(
log(N)
我打赌),以及找到它的算法的名称/运行时间?提问者:https://cs.stackexchange.com/questions/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequencesContent-Type
由浏览器自动确定 .究竟是如何确定的被问到:How is mime type of an uploaded file determined by browser?
application / x-www-form-urlencoded
现在将
enctype
更改为application/x-www-form-urlencoded
,重新加载浏览器,然后重新提交 .Firefox发送:
显然,文件数据没有发送,只有基本名称 . 所以这不能用于文件 .
至于文本字段,我们看到像
a
和b
这样的常见可打印字符是在一个字节中发送的,而不可打印的字符如0xCF
和0x89
分别占用 3 bytes :%CF%89
!比较
文件上传通常包含许多不可打印的字符(例如图像),而文本形式几乎从不这样做 .
从我们看到的例子中可以看到:
multipart/form-data
:向消息添加几个字节的边界开销,并且必须花一些时间计算它,但是在一个字节中发送每个字节 .application/x-www-form-urlencoded
:每个字段有一个字节边界(&
),但为每个不可打印的字符添加了 3x 的线性开销因子 .因此,即使我们可以发送带有
application/x-www-form-urlencoded
的文件,我们也不会这样做,因为它效率很低 .但是对于在文本字段中找到的可打印字符,它无关紧要并且产生的开销较少,因此我们只是使用它 .
将文件作为二进制内容发送(无表格或FormData上传)
在给定的答案/示例中,文件(最有可能)使用HTML表单或使用FormData API上传 . 该文件只是请求中发送的数据的一部分,因此
multipart/form-data
Content-Type
标头 .如果要将文件作为唯一内容发送,则可以直接将其添加为请求正文,并将
Content-Type
标头设置为要发送的文件的MIME类型 . 文件名可以添加到Content-Disposition
标头中 . 您可以像这样上传:如果您不(想)使用表格而您只对此感兴趣上传单个文件这是将您的文件包含在请求中的最简单方法 .
我有这个示例Java代码:
我有这个test.html文件:
最后我将用于测试目的的文件名为 a.dat ,其内容如下:
如果您将上面的字节解释为ASCII或UTF-8字符,它们实际上将代表:
让我们运行我们的Java代码,在我们最喜欢的浏览器中打开 test.html ,上传
a.dat
并提交表单,看看我们的服务器收到了什么:好吧,我看到字符 9ie 并不奇怪,因为我们告诉Java打印它们将它们视为UTF-8字符 . 您也可以选择将它们作为原始字节读取..
实际上是这里的最后一个HTTP标头 . 之后是HTTP Body,其中可以看到我们上传的文件的元和内容 .
http://www.tutorialspoint.com/http/http_messages.htm