Content-Type: application/x-www-form-urlencoded
表示纯文本表单提交方式格式如下:
POST /users HTTP/1.1
Host: api.github.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
name=zhangsan&gender=male
对应的 Retrofit 代码:
@FormUrlEncoded
@POST("/users")
Call<User> addUser(@Field("name") String name, @Field("gender") String gender);
对于 GET 请求也有可能使用这种类型。
Content-Type: multipart/form-data;boundary=[分隔符]
包含文件的多文本表单提交方式格式如下:
POST /home/upload.html HTTP/1.1
Accept: text/plain, */*
Accept-Language: zh-cn
Host: 192.168.24.56
Content-Type:multipart/form-data;boundary=-----------------------------7db372eb000e2
Content-Length: 3693
Connection: Keep-Alive
-------------------------------7db372eb000e2
Content-Disposition: form-data; name=“username”
admin
-------------------------------7db372eb000e2
Content-Disposition: form-data; name=“file”; filename=“kn.jpg”
Content-Type: image/jpeg
(此处省略jpeg文件二进制数据…)
-------------------------------7db372eb000e2–
注意这一行: Content-Type: multipart/form-data; boundary=---------------------------7db372eb000e2
,根据 RFC1867,在这种方式下,Content-Type:multipart/form-data
字段是必须的。它包括一个名为 boundary
的标志,它可以是随便输入的字符串,对后面的具体内容也是必须的,它用来分辨一段内容的开始。
Content-Length: 3693 ,这里的 3693 是要上传文件的总长度。
绿色字体部分就是需要上传的数据,可以是文本,也可以是图片等。数据内容前面需要有 Content-Disposition, Content-Type 以及 Content-Transfer-Encoding 等说明字段。
最后的紫色部分就是协议的结尾了。
---------------------------7db372eb000e2
是分隔符,分隔多个文件、表单项。其中 b372eb000e2
是即时生成的一个数字,用以确保整个分隔符不会在文件或表单项的内容中出现。
Form 每个部分用分隔符分割,分隔符之前必须加上 “--
” 这两个字符(即--{boundary}
)才能被 http 协议认为是 Form 的分隔符,表示结束的话,在正确的分隔符后面添加"--
"表示结束(即--{boundary}--
)。
前面的
---------------------------7d
是 IE 特有的标志,Mozila 为---------------------------71
每个分隔的数据的都可以用 Content-Type 来表示下面数据的类型,可以参考 rfc1341 例如 :Contect-Type:image/jpeg
表示下面的数据是jpeg
文件数据。
对应的 Retrofit 代码:
@Multipart
@POST("/users")
Call<User> addUser(@Part("name") RequestBody name, @Part("avatar") RequestBody avatar);
...
RequestBody namePart = RequestBody.create(MediaType.parse("text/plain"), nameStr);
RequestBody avatarPart = RequestBody.create(MediaType.parse("image/jpeg"), avatarFile);
api.addUser(namePart, avatarPart);
Content-Type: application/json
表示提交 json 格式的数据格式如下:
POST /users HTTP/1.1
Host: app.test.com
Content-Type: application/json; charset=utf-8
Content-Length: 38
{"name":"zhangsan","gender":"male"}
对应的 Retrofit 代码:
@POST("/users")
Call<User> addUser(@Body("user") User user);
...
// 需要使用 JSON 相关的 Converter
api.addUser(user);
响应中返回 JSON:
HTTP/1.1 200 OK
Content-type: application/json; charset=utf-8
Content-length: 234
[{"login":"mojombo","id":1,"node_id":"MDQ6VXN1 cjE=",
"avatar_url":"https://avatars0.githubuse rcontent.com/u/1?v=4",
"gravat":....}]
Content-Type: image/jpeg
表示提交二进制文件内容,上传单个文件格式如下:
POST /user/1/avatar HTTP/1.1
Host: app.test.com
Content-Type: image/jpeg
Content-Length: 1575
JFIFHH9......
对应的 Retrofit 代码:
@POST("users/{id}/avatar")
Call<User> updateAvatar(@Path("id") String id, @Body RequestBody avatar);
...
RequestBody avatarBody = RequestBody.create(MediaType.parse("image/jpeg"), avatarFile);
api.updateAvatar(id, avatarBody)
响应中返回二进制内容:
HTTP/1.1 200 OK
Content-type: image/jpeg
Content-length: 1575
JFIFHH9......
常见的 body 数据类型主要有以下四大类:
① text
: 文本格式的可读数据,如 text/html
超文本,text/plain
纯文本,text/css
样式表等
② image
: 图像文件,如 image/gif
、image/jpeg
、image/png
等
③ audio/video
: 音频和视频数据,如audio/mpeg
、video/mp4
等
⑤ application
:格式不固定,可以是文本或二进制,必须由上层应用来解释,常见的有 application/json
、application/javascript
、application/pdf
等
如果不知道数据是什么类型就用 application/octet-stream
即不透明的二进制数据
完整的类型对照表可以参考 IANA 维护的 MIME 类型列表:https://www.iana.org/assignments/media-types/media-types.xhtml
实际上我们不需要记住这些,在所使用的开发语言中有很多系统自带方法或者三方库可以直接根据文件输出其 MIME 类型。
在 Android 中,有两种自带的方法可以获取一个文件的 MIME 类型
MimeTypeMap
来获取ContentResolver
来获取下面是使用MimeTypeMap
来根据一个File
对象获取其 MIME 类型的示例代码:
import android.webkit.MimeTypeMap;
public String getMimeType(File file) {
String mimeType = null;
String extension = MimeTypeMap.getFileExtensionFromUrl(file.getAbsolutePath());
if (extension != null) {
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
}
return mimeType;
}
这个方法首先使用MimeTypeMap.getFileExtensionFromUrl()
来获取文件的扩展名,然后使用MimeTypeMap.getSingleton().getMimeTypeFromExtension()
来获取对应的 MIME 类型。最后,它返回找到的 MIME 类型字符串。
请注意,这种方法依赖于文件扩展名来确定 MIME 类型,因此文件的扩展名必须正确设置。如果文件没有扩展名或扩展名不正确,那么这个方法可能无法正确识别 MIME 类型。
但是有时我们开发中得到的是一个从系统媒体数据库中查询到的 Content Uri 对象 (例如content://media/external/images/media/94
),它是不带文件扩展名的,这时该如何获取其 MIME 类型呢?
此时可以考虑使用 ContentResolver
,下面是使用 ContentResolver
获取 Content Uri 的参考代码:
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
public String getMimeTypeFromContentUri(Context context, Uri contentUri) {
ContentResolver contentResolver = context.getContentResolver();
String mimeType = contentResolver.getType(contentUri);
return mimeType;
}
还有一种情况是,我们有一个 File
类型的 Uri
(即以 file
开头的,如 file://xxx/xx
)可以通过如下参考代码获取:
String fileExtension = MimeTypeMap.getFileExtensionFromUrl(fileUri.toString());
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension.toLowerCase());
以下参考代码可同时适用于 Content Uri 和 File Uri 的情况:
public String getMimeType(Context context, Uri uri) {
String mimeType = null;
if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
ContentResolver cr = context.getContentResolver();
mimeType = cr.getType(uri);
} else {
String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri.toString());
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension.toLowerCase());
}
return mimeType;
}