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

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

  • 数据大小
  • 存在非ASCII字符
  • 存在(未编码)的二进制数据
  • 需要传输额外的数据(如文件名)

到目前为止,我基本上没有在网上find有关使用不同内容types的正式指导。

TL; DR

故事的寓意是,如果您有二进制(非字母数字)数据(或者大小有效载荷)进行传输,请使用multipart/form-data 。 否则,使用application/x-www-form-urlencoded


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

对于application/x-www-form-urlencoded ,发送到服务器的HTTP消息的主体本质上是一个巨大的查询string – 名称/值对由&符号分隔,并且名称与等于符号( = )。 一个例子是:

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

根据规范 :

[保留]和非字母数字字符由`%HH',一个百分号和两个hex数字代表字符的ASCII码

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

这就是multipart/form-data进来的地方。使用这种传输名称/值对的方法,每对在MIME消息中被表示为“部分”(如其他答案所述)。 零件由一个特定的string边界分开(专门select,使得这个边界string不会出现在任何“值”有效载荷中)。 每个部分都有自己的一组MIME标题,如Content-Type ,特别是Content-Disposition ,它可以给每个部分的“名称”。 每个名称/值对的值片段是MIME消息的每个部分的有效载荷。 在表示有效载荷时,MIME规范给了我们更多的select – 我们可以select更有效的二进制数据编码来节省带宽(例如,基数为64甚至原始二进制)。

为什么不总是使用multipart/form-data ? 对于简单的字母数字值(像大多数Web表单),添加所有MIME头部的开销将远远超过由更有效的二进制编码节省的开销。

至less阅读第一部分!

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

这对application/x-www-form-urlencoded来说不是问题,因为没有边界。 x-www-form-urlencoded也可以总是处理二进制数据,通过简单的把一个任意字节转换成三个7BIT字节的方法。 效率低下,但它的工作(并注意关于不能发送文件名以及二进制数据的说法是不正确的;你只是发送它作为另一个键/值对)。

multipart/form-data是边界分隔符不能存在于文件数据中(参见RFC2388 ; 5.2节也包含了一个相当蹩脚的理由,因为没有适当的聚合MIMEtypes来避免这个问题)。

因此,初看起来, multipart/form-data任何file upload,二进制或其他方面都是毫无价值的。 如果你没有正确select你的边界,那么你最终会遇到一个问题,无论你是发送纯文本还是原始二进制文件 – 服务器将在错误的地方find一个边界,你的文件将被截断,或者POST会失败。

关键是select一个编码和一个边界,使得你所select的边界字符不能出现在编码输出中。 一个简单的解决scheme是使用base64 (不要使用原始二进制文件)。 在base64中, 3个任意字节被编码成4个7位字符,其中输出字符集是[A-Za-z0-9+/=] (即字母数字或'+','/','=') 。 =是一种特殊情况,并且可能只出现在编码输出的末尾,作为单个=或者一个double == 。 现在,select你的边界作为一个7位ASCIIstring,不能出现在base64输出。 你在网上看到的很多select都不能通过这个testing – MDN形成文档 ,例如,在发送二进制数据时使用“blob”作为边界 – 不好。 然而,像“!blob!” 将永远不会出现在base64输出中。

我不认为HTTP是多部分或x-www-form-urlencoded POST的限制。 Content-Type Header与HTTP POST方法正交(可以填写适合您的MIMEtypes)。 对于典型的基于HTML表示的webapps也是如此(例如,json有效载荷变得非常stream行,用于发送对于Ajax请求的有效载荷)。

关于Restful API over HTTP,我最常用的内容types是application / xml和application / json。

应用程序/ XML:

  • data-size:XML非常详细,但通常在使用压缩时并不是问题,并且认为写入访问情况(例如通过POST或PUT)比读取访问更为罕见(在许多情况下,它是所有stream量的3% )。 在那里我不得不优化写性能的情况很less
  • 是否存在非ASCII字符:可以使用utf-8作为XML中的编码
  • 二进制数据的存在:需要使用base64编码
  • 文件名数据:你可以用XML封装这个内部字段

应用程序/ JSON

  • 数据大小:比XML更紧凑,还是文本,但是可以压缩
  • 非ASCII字符:json是utf-8
  • 二进制数据:base64(也见json-binary-question )
  • 文件名数据:在json里封装成自己的字段

二进制数据为自己的资源

我会尝试将二进制数据表示为自己的资产/资源。 它增加了另一个呼叫,但更好地分离的东西。 示例图像:

POST /images Content-type: multipart/mixed; boundary="xxxx" ... multipart data 201 Created Location: http://imageserver.org/../foo.jpg  
POST /images Content-type: multipart/mixed; boundary="xxxx" ... multipart data 201 Created Location: http://imageserver.org/../foo.jpg 

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

<main-resource> ... <link href="http://imageserver.org/../foo.jpg"/> </main-resource>
<main-resource> ... <link href="http://imageserver.org/../foo.jpg"/> </main-resource> 

曼努埃尔说过的我很同意。 实际上,他的评论指的是这个url…

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

其中说:

内容types“application / x-www-form-urlencoded”对于发送大量的包含非ASCII字符的二进制数据或文本是不够的。 内容types“multipart / form-data”应该用于提交包含文件,非ASCII数据和二进制数据的表单。

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

  • 你期望你的API用户使用哪些工具和框架构build应用程序?
  • 他们是否有可以使用的框架或组件?

如果你清楚了解你的用户,以及他们将如何使用你的API,那么这将帮助你决定。 如果你为API用户上传文件很困难,那么他们会离开,你将花费大量的时间来支持他们。

除此之外,您将可以获得用于编写API的工具支持,以及如何容易地使用一种上传机制。

上传HTML5canvas图片数据只是一点点提示:

我正在为一家印刷厂开发一个项目,并且由于将图像上传到来自HTML5 canvas元素的服务器而出现了一些问题。 我挣扎了至less一个小时,并没有得到它在我的服务器上正确保存图像。

一旦我将jQuery ajax调用的contentType选项设置为application/x-www-form-urlencoded一切都正确,base64编码的数据被正确解释并成功保存为图像。


也许这有助于某人!