【转】从原理角度解析Android (Java) http 文件上传
1.这篇blog是参考别人的blog。
了解到Android的没有自己的上传文件的机制,只有去模仿web端文件上传的方法
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/23781773
文件上传是我们项目中经常使用的功能,一般我们的服务器可能都是web服务器,当我们使用非浏览器客户端上传文件时,比如手机(Android)等上传,可能就需要对传输的数据进行规范化的拼接,说白了,就是我们得自己完成浏览器帮我们做的事。
我首先写了服务器端代码,用来接收我们的数据,一会会贴出源码。然后写了个web页面用于上次,便于我们看其中的原理。
当点击了上传以后,这里我使用了firefox的firebug来观察网络信息,可以看到发出了一个POST请求,下面我框出的是请求头信息。里面包含一些请求的配置数据。
接下来看这张图:
我们可以看到我们发送的数据,一个是name为username的普通表单数据,一个为name为uploadFile的一个文件数据,可以看得出来,浏览器把文件数据转化成了2进制然后按特定的格式发给服务器了。
好了,下面开始实现上传,模拟浏览器的操作。
1、使用HttpUrlConnection
1 private static final String BOUNDARY = "----WebKitFormBoundaryT1HoybnYeFOGFlBR"; 2 3 /** 4 * 5 * @param params 6 * 传递的普通参数 7 * @param uploadFile 8 * 需要上传的文件名 9 * @param fileFormName 10 * 需要上传文件表单中的名字 11 * @param newFileName 12 * 上传的文件名称,不填写将为uploadFile的名称 13 * @param urlStr 14 * 上传的服务器的路径 15 * @throws IOException 16 */ 17 public void uploadForm(Map<String, String> params, String fileFormName, 18 File uploadFile, String newFileName, String urlStr) 19 throws IOException { 20 if (newFileName == null || newFileName.trim().equals("")) { 21 newFileName = uploadFile.getName(); 22 } 23 24 StringBuilder sb = new StringBuilder(); 25 /** 26 * 普通的表单数据 27 */ 28 for (String key : params.keySet()) { 29 sb.append("--" + BOUNDARY + "\r\n"); 30 sb.append("Content-Disposition: form-data; name=\"" + key + "\"" 31 + "\r\n"); 32 sb.append("\r\n"); 33 sb.append(params.get(key) + "\r\n"); 34 } 35 /** 36 * 上传文件的头 37 */ 38 sb.append("--" + BOUNDARY + "\r\n"); 39 sb.append("Content-Disposition: form-data; name=\"" + fileFormName 40 + "\"; filename=\"" + newFileName + "\"" + "\r\n"); 41 sb.append("Content-Type: image/jpeg" + "\r\n");// 如果服务器端有文件类型的校验,必须明确指定ContentType 42 sb.append("\r\n"); 43 44 byte[] headerInfo = sb.toString().getBytes("UTF-8"); 45 byte[] endInfo = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("UTF-8"); 46 System.out.println(sb.toString()); 47 URL url = new URL(urlStr); 48 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 49 conn.setRequestMethod("POST"); 50 conn.setRequestProperty("Content-Type", 51 "multipart/form-data; boundary=" + BOUNDARY); 52 conn.setRequestProperty("Content-Length", String 53 .valueOf(headerInfo.length + uploadFile.length() 54 + endInfo.length)); 55 conn.setDoOutput(true); 56 57 OutputStream out = conn.getOutputStream(); 58 InputStream in = new FileInputStream(uploadFile); 59 out.write(headerInfo); 60 61 byte[] buf = new byte[1024]; 62 int len; 63 while ((len = in.read(buf)) != -1) 64 out.write(buf, 0, len); 65 66 out.write(endInfo); 67 in.close(); 68 out.close(); 69 if (conn.getResponseCode() == 200) { 70 System.out.println("上传成功"); 71 } 72 73 }
我详细解释一下,首先我拼接了需要发送的数据,其实就是咱们在图三中看到的数据,然后使用HttpUrlConnetion设置了一系列属性其实就是在设置图二中看到的请求头信息。
于是,我们完成了请求头的设置,以及需要上传数据的拼接,所以我们完成了浏览器的工作,自然就实现文件上传了。
2、使用Socket实现文件上传,参数基本一致,使用HttpUrlConnection上传有一个很致命的问题就是,当上传文件很大时,会发生 内存溢出,手机分配给我们app的内存更小,所以就更需要解决这个问题,于是我们可以使用Socket模拟POST进行HTTP文件上传。
1 /** 2 * 3 * @param params 4 * 传递的普通参数 5 * @param uploadFile 6 * 需要上传的文件名 7 * @param fileFormName 8 * 需要上传文件表单中的名字 9 * @param newFileName 10 * 上传的文件名称,不填写将为uploadFile的名称 11 * @param urlStr 12 * 上传的服务器的路径 13 * @throws IOException 14 */ 15 public void uploadFromBySocket(Map<String, String> params, 16 String fileFormName, File uploadFile, String newFileName, 17 String urlStr) throws IOException { 18 if (newFileName == null || newFileName.trim().equals("")) { 19 newFileName = uploadFile.getName(); 20 } 21 22 StringBuilder sb = new StringBuilder(); 23 /** 24 * 普通的表单数据 25 */ 26 27 if (params != null) 28 for (String key : params.keySet()) { 29 sb.append("--" + BOUNDARY + "\r\n"); 30 sb.append("Content-Disposition: form-data; name=\"" + key 31 + "\"" + "\r\n"); 32 sb.append("\r\n"); 33 sb.append(params.get(key) + "\r\n"); 34 } else{ab.append("\r\n");} 35 /** 36 * 上传文件的头 37 */ 38 sb.append("--" + BOUNDARY + "\r\n"); 39 sb.append("Content-Disposition: form-data; name=\"" + fileFormName 40 + "\"; filename=\"" + newFileName + "\"" + "\r\n"); 41 sb.append("Content-Type: image/jpeg" + "\r\n");// 如果服务器端有文件类型的校验,必须明确指定ContentType 42 sb.append("\r\n"); 43 44 byte[] headerInfo = sb.toString().getBytes("UTF-8"); 45 byte[] endInfo = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("UTF-8"); 46 47 System.out.println(sb.toString()); 48 49 URL url = new URL(urlStr); 50 Socket socket = new Socket(url.getHost(), url.getPort()); 51 OutputStream os = socket.getOutputStream(); 52 PrintStream ps = new PrintStream(os, true, "UTF-8"); 53 54 // 写出请求头 55 ps.println("POST " + urlStr + " HTTP/1.1"); 56 ps.println("Content-Type: multipart/form-data; boundary=" + BOUNDARY); 57 ps.println("Content-Length: " 58 + String.valueOf(headerInfo.length + uploadFile.length() 59 + endInfo.length)); 60 ps.println("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); 61 62 InputStream in = new FileInputStream(uploadFile); 63 // 写出数据 64 os.write(headerInfo); 65 66 byte[] buf = new byte[1024]; 67 int len; 68 while ((len = in.read(buf)) != -1) 69 os.write(buf, 0, len); 70 71 os.write(endInfo); 72 73 in.close(); 74 os.close(); 75 }
这里因为我们使用的是Socket,所以自然对于请求头,我们也需要自己拼接了,没有什么属性设置了。参考图二框出的部分,我们使用 PrintStream完成了请求头的拼接,接下来就是数据的拼接,这和使用HttpUrlConnection的方式一致。我们也完成了数据的上传。
最后测试我们的代码:
1 public static void main(String[] args) { 2 try { 3 4 File file = new File("D:/dtd", "dwr30.dtd"); 5 6 new Test().uploadForm(null, "uploadFile", file, "helloworld.txt", 7 "http://localhost:8080/strurts2fileupload/uploadAction"); 8 9 new Test().uploadFromBySocket(null, "uploadFile", file, 10 "hibernate-configuration-3.0.dtd", 11 "http://localhost:8080/strurts2fileupload/uploadAction"); 12 13 } catch (Exception e) { 14 e.printStackTrace(); 15 } 16 }
效果:
如果这篇文章对你有帮助,赞一下~

浙公网安备 33010602011771号