You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# 请求体
--${Boundary}
Content-Disposition: form-data; name="name of file"
Content-Type: application/octet-stream
bytes of file
--${Boundary}
Content-Disposition: form-data; name="name of pdf"; filename="pdf-file.pdf"
Content-Type: application/octet-stream
bytes of pdf file
--${Boundary}
Content-Disposition: form-data; name="key"
Content-Type: text/plain;charset=UTF-8
text encoded in UTF-8
--${Boundary}--
一些姿势点
Boundary:Part的分界,由一段随机字符串组成
Part:由 Content-Disposition: form-data; name="{part name}"作为头内容,和HTTP的头相似
Other attributes:比如二进制文件,我们需要定义filename,否则不会被解析为文件!
Charset of text in form data:每一个part有自己独立的Content-Type可以加上charset支持不同的编码规则。
Denotes a single part of a multi-part request.
The parameter type on which this annotation exists will be processed in one of three ways:
If the type is okhttp3.MultipartBody.Part the contents will be used directly. Omit the name from the annotation (i.e., @part MultipartBody.Part part).
If the type is RequestBody the value will be used directly with its content type. Supply the part name in the annotation (e.g., @part("foo") RequestBody foo).
Other object types will be converted to an appropriate representation by using a converter. Supply the part name in the annotation (e.g., @part("foo") Image photo).
Values may be null which will omit them from the request body.
起源,Restful中的非主流
本篇博客篇幅是安排是知识性和具体技术细节对半开。
目前我们几乎所有前端对接后端的api基本都遵循Restful的原则。不知道你有没有碰到过这样的场景。
既要又要,熟悉的节奏。往往这个时候我们设计协议就会陷入困境。
有个折中一点的方式就是把信息带到url的path里面去。但是如果信息数据多,而且分成好多个字段怎么办?
Content-Type: multipart/form-data闪亮登场
RFC文档里面有对multipart/form-data详细定义
不过看文档实在是太枯燥了,我们看下大致格式
Boundary:Part的分界,由一段随机字符串组成
Part:由 Content-Disposition: form-data; name="{part name}"作为头内容,和HTTP的头相似
Other attributes:比如二进制文件,我们需要定义filename,否则不会被解析为文件!
Charset of text in form data:每一个part有自己独立的Content-Type可以加上charset支持不同的编码规则。
其实 multipart/form-data在90年代就已经提出了!
可以从RFC2046里面看出
当时我们网页用的都是html,对于填写表单上传的场景。我们除了填写姓名、性别、学历 balabala等等信息以外,很可能还要上传一张报名照。这种二进制文件和结构化数据正好对应multipart/form-data的应用场景
似曾相识的multipart/form-data
multipart/form-data并非第一次见,其实以前已经在使用很多年了!
以前用.net的时候用过WebClient上传文件,如果你用截包的方式查看报文,你可以发现用的就是multipart/form-data格式
不过API确是一次只能上传一个文件,其实完全可以支持一次性上传多个文件。白瞎了multipart协议啊!后面的HttpClient的内可以支持对multipart form更精细的控制,这里暂时不介绍了。
我们接下来操作相对陌生一点的安卓领域。
安卓使用retrofit2组建完成multipart/form-data数据交互
retrofit对multipart的文档写得特别的简单
需要和源码和注释一起看,比如Part的注释
这里详细解释一下
Part注解根据参数类型分为三种情况
如果不举例子可能会“没头没脑”
这里retrofit使用了Json转换器,这样任何其他对象都会转为类型为application/json,内容是json字符串。
比如有个上传日志的api,既要上传设备详细信息,又要上传二进制日志文件。
那么retrofit的api的接口就要定义如下
接下来看看参数生成部分代码
可以看到文本字段都用"text/plain"生成RequestBody搞定啦,注意这里charset没有设,retrofit会自动帮我设置为UTF-8格式
二进制部分MultipartBody.Part生成的时候,需要自己设置name和filename,content-type使用application/octet-stream
关于filename的问题
其实一开始我文件不是直接用okhttp3.MultipartBody.Part而是用okhttp3.RequestBody生成的。主要问题是filename不会帮我自动加,造成服务端不能正确解析为文件。
这里retrofit的issue区里面有激烈讨论,有一个”非常难看“的解决方案
这个filename是“硬”加上去的。
相关链接
而且我的filename是“动态”的,权衡利弊下还是用回我的okhttp3.MultipartBody.Part吧!
The text was updated successfully, but these errors were encountered: