Android - 上传文件实现方法

一月份做项目时 Android 客户端需要上传图片,小小的研究了下,不改独享,分享!

如果要在客户端向服务器上传文件,我们就必须模拟一个POST multipart/form-data类型的请求,Content-Type必须是multipart/form-data
这篇详细介绍这个 ENCTYPE=”multipart/form-data” 协议,来解决自己的Java程序向服务器传文件的问题(比如:安卓程序上传图片)

ENCTYPE=”multipart/form-data” :通过 http 协议上传文件 rfc1867协议,客户端发送内容构造。

1
2
3
4
5
6
<form action="picture/upload.do" method="POST" ENCTYPE="multipart/form-data">
<input type="text" name="users_id">
<input type="text" name="users_name">
<input type="file" name="image_path">
<input type="submit">
</form>

使用上面这个表单,抓包的结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
POST /picture/upload.do HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*
Referer: http://192.168.1.98:8080/uploadpic.jsp
Accept-Language: zh-CN
User-Agent: Mozilla/4.0
Content-Type: multipart/form-data; boundary=---------------------------7dd36df20300
Accept-Encoding: gzip, deflate
Host: 192.168.1.98:8080
Content-Length: 529762
Connection: Keep-Alive
Cache-Control: no-cache
-----------------------------7dd36df20300
Content-Disposition: form-data; name="users_id"
<!--空行-->
1
-----------------------------7dd36df20300
Content-Disposition: form-data; name="users_name"
<!--空行-->
admin
-----------------------------7dd36df20300
Content-Disposition: form-data; name="image_path"; filename="S21223-170317.jpg"
Content-Type: image/pjpeg
<!--空行-->
<!--文件 byte 数据-->
-----------------------------7dd36df20300--
<!--空行-->

###详细解析:

  • Content-Type: multipart/form-data; boundary=—————————7dd36df20300
    根据 rfc1867, multipart/form-data是必须的。
    —————————7dd36df20300 是分隔符,分隔多个文件、表单项。
    其中 d36df20300 是即时生成的一个字符,用以确保整个分隔符不会在文件或表单项的内容中出现。
    —————————7d IE内核标志
    —————————71 Mozila内核标志
  • 从请求体里可以看出每一条数据后面都有换行 \r\n ,并且 Parameter 和 value 之间还有个空行

####最后把格式提取:

1
2
3
4
5
6
7
8
9
10
11
--分隔符 \r\n
Content-Disposition: form-data; name="[Parameter]" \r\n
\r\n
[value] \r\n
--分隔符 \r\n
Content-Disposition: form-data; name="[Parameter]"; filename="[]" \r\n
Content-Type: image/pjpeg \r\n
\r\n
<!--文件 byte 数据--> \r\n
--分隔符-- \r\n
\r\n

##程序实现传输协议:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
String path = "http://192.168.1.102:8080/picture/upload.do";
// 换行符
String enter = "\r\n";
// 分割线
String boundary = "---------------------------7dd36df20300";
StringBuffer sb = new StringBuffer();
// 获取文件流
File file = new File("D:\\123.jpg");
FileInputStream is = new FileInputStream(file);
// 参数信息
String[] keys = { "users_id", "users_name" };
String[] values = { "1", "admin" };
// 拼凑参数
for (int i = 0; i < keys.length; i++) {
sb.append("--" + boundary + enter);
sb.append("Content-Disposition: form-data; name=\"" + keys[i] + "\"");
sb.append(enter + enter);
sb.append(values[i]);
sb.append(enter);
}
// 拼凑文件头部信息
sb.append("--" + boundary);
sb.append(enter);
sb.append("Content-Disposition: form-data; name=\"image_path\"; filename=\"123.jpg\"");
sb.append(enter);
sb.append("Content-Type: image/*");
sb.append(enter + enter);
byte[] data = sb.toString().getBytes();
byte[] end_data = (enter + "--" + boundary + "--" + enter + enter).getBytes();
// 初始化 HttpURLConnection
URL url = new URL(path);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(3000);
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
con.setRequestMethod("POST");
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Charset", "UTF-8");
con.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
con.setRequestProperty("Content-Length", file.length() + data.length + end_data.length + "");
// 开始上传
OutputStream outputStream = con.getOutputStream();
outputStream.write(data);
byte[] fileBuffer = new byte[1024];
int length = -1;
while ((length = is.read(fileBuffer)) != -1) {
outputStream.write(fileBuffer, 0, length);
}
is.close();
outputStream.write(end_data);
outputStream.flush();
outputStream.close();
con.getResponseCode();

Gavin Liu wechat
欢迎您扫一扫上面的二维码,订阅我的微信公众号!