我有一个用于文件上传的API,需要提交多部分表单。但是我有一个客户在编写客户端,他的系统无法正确生成multipart/form-data
请求。他要求我修改我的API以接受application/x-www-form-urlencoded
请求中的文件,文件名在一个键/值对中,文件内容base64编码在另一个键/值对中。
原则上我可以很容易地做到这一点(之后我需要淋浴),但我担心尺寸限制。我们在Production中预期的文件相当大:5-10MB,有时高达20MB。我找不到任何告诉我表格POST中个别键/值对数据长度限制的内容,无论是在规格中(我已经看过,HTTP spec和Forms spec )或在特定实现中(我的API在Java应用程序服务器Jetty上运行,前面有一个Apache HTTP服务器)。
POST表单中键/值对中单个值的技术和实际限制是什么?
答案 0 :(得分:0)
HttpConfiguration类上存在人为限制,配置。最大数量的密钥和请求正文内容的最大大小。
实际上,这是一个非常糟糕的主意。
您将拥有一个String,每个字符使用2个字节作为Base64数据。 而你只有33%的开销只是Base64。
他们还必须对各种特殊字符(例如" +"它们在Base64中有意义,但是空格"" in)中的utf8 urlencode urlencoded form。因此他们需要将" +"编码为"%2B")。
因此,对于一个20MB的文件,您将拥有......
20,971,520字节的原始数据,在原始Base64中表示为27,892,122个字符,在urlencoded时使用(平均)29,286,728个字符,它将以字符串形式使用58,573,455字节的内存。
Jetty上的解码过程将获取传入的原始urlencoded字节,并在解码urlencoded格式之前在String中分配2x大小。所以这是一个长度为58,573,456的java.lang.String(它为字符串使用了117,146,912字节的堆内存,并且不要忘记保存29MB的字节缓冲数据!)只是为了将Base64二进制文件解码为x-www-form-urlencoded字符串形式的值。
我会反击并强制他们正确使用multipart/form-data
。有很多好的库可以正确地生成表单数据。
如果他们使用Java,请告诉他们使用Apache HttpComponents项目中的httpmime
库(他们不必使用/使用/安装Apache Http Client来使用httpmime,它是一个独立的库)。
替代方法
没有必要说你必须使用application/x-www-form-urlecnoded
或multipart/form-data
。
通过application/octet-stream
他们使用POST
,并且必须包含以下有效请求标头...
Connection: close
Content-Type: application/octet-stream
Content-Length: <whatever_size_the_content_is>
Connection: close
表示http协议何时完成。Content-Type: application/octet-stream
表示Jetty不会将该内容作为请求参数处理,也不会对其应用字符集翻译。Content-Length
是确保发送/接收整个文件所必需的。然后将原始二进制字节流式传输给您。
这仅适用于文件内容,如果您有其他需要传入的信息(例如文件名),请考虑使用查询参数或自定义请求标头(例如:X-Filename: secretsauce.doc
)
在您的servlet上,您只需使用HttpServletRequest.getInputStream()来获取这些字节,并使用Content-Length
变量来验证您是否收到了整个文件。
或者,您可以让它们在请求标头中提供SHA1哈希值,例如X-Sha1Sum: bed0213d7b167aa9c1734a236f798659395e4e19
,然后您可以使用它来验证整个文件是否已正确发送/接收。