【转】从原理角度解析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     }


效果:




如果这篇文章对你有帮助,赞一下~

 

posted @ 2015-09-08 16:18  perfect亮  阅读(334)  评论(0)    收藏  举报