当前位置:  开发笔记 > 编程语言 > 正文

application/x-www-form-urlencoded或multipart/form-data?

如何解决《application/x-www-form-urlencoded或multipart/form-data?》经验,为你挑选了4个好方法。

在HTTP中有两种POST数据的方式:application/x-www-form-urlencodedmultipart/form-data.据我所知,大多数浏览器只能在使用时上传文件multipart/form-data.在API上下文中使用其中一种编码类型时是否有任何其他指导(不涉及浏览器)?这可能基于:

数据大小

存在非ASCII字符

存在于(未编码的)二进制数据上

需要传输额外的数据(如文件名)

到目前为止,我基本上没有在网上找到有关使用不同内容类型的正式指导.



1> Matt Bridges..:

TL; DR

摘要; 如果你有二进制(非字母数字)数据(或一个很大的有效载荷)要传输,请使用multipart/form-data.否则,请使用application/x-www-form-urlencoded.


您提到的MIME类型Content-Type是用户代理(浏览器)必须支持的HTTP POST请求的两个标头.这两种类型的请求的目的是向服务器发送名称/值对列表.根据传输的数据类型和数量,其中一种方法比另一种方法更有效.要理解为什么,你必须看看每个人在做什么.

因为application/x-www-form-urlencoded,发送到服务器的HTTP消息的主体基本上是一个巨大的查询字符串 - 名称/值对由&符号(&)分隔,名称通过等号(=)与值分隔.一个例子是: 

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

根据规格:

[保留和]非字母数字字符替换为'%HH',百分号和两个十六进制数字表示字符的ASCII代码

这意味着对于我们其中一个值中存在的每个非字母数字字节,它将需要三个字节来表示它.对于大型二进制文件,有效载荷增加三倍将是非常低效的.

这就是multipart/form-data进入的地方.通过这种传输名称/值对的方法,每对在MIME消息中表示为"部分"(如其他答案所述).部件由特定的字符串边界分隔(具体选择使得此边界字符串不会出现在任何"值"有效负载中).每个部分都有自己的一组MIME标题Content-Type,特别是Content-Disposition,它们可以为每个部分提供"名称".每个名称/值对的值片段是MIME消息的每个部分的有效负载.MIME规范在表示值有效负载时为我们提供了更多选项 - 我们可以选择更有效的二进制数据编码来节省带宽(例如base 64甚至原始二进制).

为什么不一直使用multipart/form-data?对于简短的字母数字值(与大多数Web表单一样),添加所有MIME头的开销将大大超过更有效的二进制编码所带来的节省.


x-www-form-urlencoded有长度限制,还是无限制?
@Pacerier限制由接收POST请求的服务器强制执行.有关更多讨论,请参阅此主题:http://stackoverflow.com/questions/2364840/what-is-the-size-limit-of-a-post-request
另请注意,如果表单包含命名文件上载,则您唯一的选择是form-data,因为urlencoded无法放置文件名(在form-data中,它是content-disposition的name参数).
@ZiggyTheHamster JSON和BSON对于不同类型的数据都更有效.对于两种序列化方法,Base64都不如gzip.Base64根本没有任何优势,HTTP支持二进制pyload.
@EML看到我的括号"(具体选择使得此边界字符串不会出现在任何"值"有效载荷中)"

2> EML..:

在这里阅读至少第一个PARA!

我知道这已经太晚了3年,但马特的(接受的)答案是不完整的,最终会让你陷入困境.这里的关键是,如果您选择使用multipart/form-data,边界不得出现在服务器最终收到的文件数据中.

这不是问题application/x-www-form-urlencoded,因为没有边界.x-www-form-urlencoded通过将一个任意字节转换为三个7BIT字节的简单方法,也可以始终处理二进制数据.效率不高,但它有效(并注意关于无法发送文件名以及二进制数据的注释不正确;您只需将其作为另一个键/值对发送).

问题multipart/form-data在于边界分隔符不得出现在文件数据中(参见RFC 2388 ; 5.2节还包含一个相当蹩脚的借口,因为没有适当的聚合MIME类型可以避免这个问题).

因此,乍一看,multipart/form-data任何文件上传,二进制或其他方面都没有任何价值.如果不正确地选择你的边界,那么你最终有一个问题,不管你是发送纯文本或原始二进制-服务器会发现放错了地方的边界,你的文件将被截断,或POST将失败.

关键是选择编码和边界,使选定的边界字符不会出现在编码输出中.一个简单的解决方案是使用base64(千万不能使用原始的二进制).在base64中, 3个任意字节被编码为4个7位字符,其中输出字符集是[A-Za-z0-9+/=](即字母数字,'+','/'或'=').=是一种特殊情况,可能只出现在编码输出的末尾,如单个=或双精度==.现在,选择您的边界作为7位ASCII字符串,它不能出现在base64输出中.您在网上看到的许多选择都无法通过此测试 - 例如,MDN表单文档在发送二进制数据时使用"blob"作为边界 - 不是很好.但是,像"!blob!"之类的东西.将永远不会出现在base64输出中.


虽然考虑多部分/表格数据是确保边界不出现在数据中,但通过选择足够长的边界来实现这是相当简单的.请不要使用我们的base64编码来完成此任务.随机生成的边界和与UUID相同的长度应该足够:http://stackoverflow.com/questions/1705008/simple-proof-that-guid-is-not-unique.
这个答案不仅不会给讨论增加任何内容,而且会给出错误的建议.首先,每当在分开的部分中发送随机数据时,所选边界总是可能存在于有效载荷中.确保不会发生这种情况的唯一方法是检查我们提出的每个边界的整个有效负载.完全不切实际.我们只接受碰撞的_infinitesimal_概率并得出一个合理的边界,比如"--- boundary- -boundary ---".其次,总是使用Base64会浪费带宽并毫无理由地填充缓冲区.
@EML,这根本没有意义.显然,http客户端(浏览器)会自动选择边界,客户端将足够聪明,不会使用与上传文件内容冲突的边界.这是一个简单的子字符串匹配`index === -1`.
@Pacerier :(一)阅读问题:"不涉及浏览器,API上下文".(B)浏览器无论如何都不会为您构建请求.你手动完成.浏览器没有神奇之处.
@BeniBela,他可能会建议使用`'()+ - ./:=`然后.然而,带子串检查的随机生成仍然是可行的方法,可以用一行来完成:`while(true){r = rand(); if(data.indexOf(r)=== -1){doStuff(); break;}}`.EML的建议(转换为base64只是为了避免匹配子串)简直就是奇怪,更不用说它带来了不必要的性能下降.而且,由于单线算法同样简单易行,所以无所不用.Base64并不意味着(ab)以这种方式使用,因为HTTP主体[接受所有8位](http://goo.gl/L94Qcm)八位位组.
关于`!blob!`:RFC 1341说`!`不是边界中允许的字符.只能使用`0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'()+, - ./:=?`
出于同样的原因,Avro数据文件使用足够长的同步标记(http://apache-avro.679487.n3.nabble.com/Synchronization-Markers-td4026016.html)是安全的,数据的可能性非常小相对于数据中心被灾难消耗的风险,与128位真正随机数据的冲突无需担心.
迟到总比不到好。
@Pacerier:这是什么语言?你是说`Math.random()`吗?看-如果您想使用随机数,可以。生成一个随机的6字节序列,将其用作分隔符,而不必费心检查它,如Joshcodes所建议的那样。只要是随机的,而不是您刚刚键入的任意文本序列,就可以了。另一方面,如果您不喜欢统计编程,并且可以占用base64的17%的开销,则只需使用保证的分隔符,如`!blob!`。我没有告诉您如何做-大多数人在实践中会使用随机字节序列。你的来电。
@EML,再次阅读你的第一段.你说它"最终会让你陷入麻烦".关键在于我们不会遇到麻烦.我正确地指出你的上述说法是错误的,但你仍然坚持认为我们会遇到麻烦.2)我正确地说明浏览器会自动选择边界,并使用websniffer自行验证.3)没有人说你说这是一个难题.我问了一个问题,*仍然*没有得到答复:这么难做吗?while(true){r = rand(); if(data.indexOf(r)=== -1){doStuff(); break;}}`?

3> manuel aldan..:

我认为HTTP不限于多部分或x-www-form-urlencoded中的POST.的内容类型标头是垂直于HTTP POST方法(可以填充适合你MIME类型).对于典型的基于HTML表示的webapps也是如此(例如,json有效载荷变得非常流行以传输ajax请求的有效载荷).

关于HTTP上的Restful API,我接触过的最流行的内容类型是application/xml和application/json.

应用程序/ XML:

data-size:XML非常冗长,但在使用压缩时通常不会出现问题,并认为写访问案例(例如通过POST或PUT)作为读访问更为罕见(在许多情况下,它仅占所有流量的3%) ).很少有我必须优化写入性能的情况

存在非ascii字符:你可以在XML中使用utf-8作为编码

二进制数据的存在:需要使用base64编码

文件名数据:您可以在XML中封装此内部字段

应用程序/ JSON

data-size:比XML更紧凑,仍然是文本,但你可以压缩

非ascii chars:json是utf-8

二进制数据:base64(另见json-binary-question)

filename data:在json中封装为自己的field-section

二进制数据作为自己的资源

我会尝试将二进制数据表示为自己的资产/资源.它增加了另一个电话,但更好地解耦了.示例图片:

POST /images
Content-type: multipart/mixed; boundary="xxxx" 
... multipart data

201 Created
Location: http://imageserver.org/../foo.jpg  

在以后的资源中,您可以简单地将二进制资源内联为链接:




我认为OP可能只是询问HTML表单使用的两种类型,但我很高兴指出这一点.
application/x-www-form-urlencoded是您请求的默认mime类型(另请参见http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4).我将它用于"普通"webforms.对于API,我使用application/xml | json.multipart/form-data是一个考虑附件的钟(在响应体内部,几个数据部分与定义的边界字符串连接).

4> Martin Peck..:

我同意曼努埃尔的说法.事实上,他的评论指的是这个网址......

http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4

...说明:

内容类型"application/x-www-form-urlencoded"对于发送大量二进制数据或包含非ASCII字符的文本效率低下.内容类型"multipart/form-data"应该用于提交包含文件,非ASCII数据和二进制数据的表单.

但是,对我来说,它将归结为工具/框架支持.

您希望API用户使用哪些工具和框架来构建他们的应用程序?

他们是否有可以使用的框架或组件支持一种方法而不是另一种方法?

如果您清楚了解您的用户,以及他们将如何使用您的API,那么这将有助于您做出决定.如果您为API用户努力上传文件,那么他们就会离开,您将花费大量时间来支持它们.

除此之外,您将拥有编写API的工具支持,以及您可以轻松地将一个上载机制容纳在另一个上面.


@GMsoF,这是可选的.请参见http://stackoverflow.com/a/16693884/632951.在制定特定服务器的特定请求时,您可能希望避免使用内容类型,以避免通用开销.
推荐阅读
爱唱歌的郭少文_
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有