背景
一月份做项目时 Android 客户端需要上传图片,小小的研究了下
如果要在客户端向服务器上传文件,我们就必须模拟一个 POST multipart/form-data
类型的请求,Content-Type
必须是 multipart/form-data
通过编写一个网页的文件上传程序,抓包看看协议规则
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 是分隔符,分隔多个文件、表单项
- 7d 内核标志
- d36df20300 是即时生成的一个字符,用以确保整个分隔符不会在文件或表单项的内容中出现
- 从请求体里可以看出每一条数据后面都有
换行
,并且 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();
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();
|