前面我们讲了如何通过 volley 实现表单的提交,而这篇文章跟上一篇衔接很大,如果没有看上一篇 blog 的朋友,建议先去看看 Android Volley解析(二)之表单提交篇
因为文件上传实质就是表单的提交,只不过它提交的数据包含文件类型,接下来还是按照表单提交的套路来分析。
数据格式
这里我们通过图片上传的案例来分析,其他文件也是同样的实现方式;以下是我在传图网传图时,上传的数据格式,先来分析一下
<code class="hljs http has-numbering"><span class="hljs-request">POST <span class="hljs-string">http://chuantu.biz/upload.php</span> HTTP/1.1</span> <span class="hljs-attribute">Host</span>: <span class="hljs-string">chuantu.biz</span> <span class="hljs-attribute">Connection</span>: <span class="hljs-string">keep-alive</span> <span class="hljs-attribute">Content-Length</span>: <span class="hljs-string">4459</span> <span class="hljs-attribute">Cache-Control</span>: <span class="hljs-string">max-age=0</span> <span class="hljs-attribute">Accept</span>: <span class="hljs-string">text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8</span> <span class="hljs-attribute">Origin</span>: <span class="hljs-string">http://chuantu.biz</span> <span class="hljs-attribute">User-Agent</span>: <span class="hljs-string">Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36</span> <span class="hljs-attribute">Content-Type</span>: <span class="hljs-string">multipart/form-data; boundary=----WebKitFormBoundaryS4nmHw9nb2Eeusll</span> <span class="hljs-attribute">Referer</span>: <span class="hljs-string">http://chuantu.biz/</span> <span class="hljs-attribute">Accept-Encoding</span>: <span class="hljs-string">gzip, deflate</span> <span class="hljs-attribute">Accept-Language</span>: <span class="hljs-string">zh-CN,zh;q=0.8</span> <span class="hljs-attribute">Cookie</span>: <span class="hljs-string">__cfduid=d9215d649e6e648e0eac7688b406a3d911425089350</span> <span class="ruby">------<span class="hljs-constant">WebKitFormBoundaryS4nmHw9nb2Eeusll</span> <span class="hljs-constant">Content</span>-<span class="hljs-constant">Disposition</span><span class="hljs-symbol">:</span> form-data; name=<span class="hljs-string">"uploadimg"</span>; filename=<span class="hljs-string">"spark_bg.png"</span> <span class="hljs-constant">Content</span>-<span class="hljs-constant">Type</span><span class="hljs-symbol">:</span> image/png <span class="hljs-constant">JFIFC</span> %<span class="hljs-comment"># , #&')*)-0-(0%()(C</span> (((((((((((((((((((((((((((((((((((((((((((((((((((<span class="hljs-string">" }!1AQa"</span>q2<span class="hljs-comment">#BR$3br</span> %&<span class="hljs-string">'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'</span>()*<span class="hljs-number">56789</span><span class="hljs-symbol">:CDEFGHIJSTUVWXYZcdefghijstuvwxyz?PNG</span> ------<span class="hljs-constant">WebKitFormBoundaryS4nmHw9nb2Eeusll</span>--</span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li></ul>
不难发现,这种格式跟表单提交的格式非常接近,不过还是有所差别,这里仔细看还是能看出来总共有加上结尾行,有五行,因为乱码部分,其实就是图片的二进制数,整个算一行;下面来分析下:
1、第一行:"--" + boundary + "\r\n"
;
前面也说了文件上传,其实就是表单提交,所以在提交数据的开始标志不变;
2、第二行:Content-Disposition: form-data; name="参数的名称"; filename="上传的文件名" + "\r\n"
这里比普通的表单多了一个filename=”上传的文件名”;
3、第三行:Content-Type: 文件的 mime 类型 + "\r\n"
这一行是文件上传必须要的,而普通的文字提交可有可无,mime 类型需要根据文档查询;
4、第四行:"\r\n"
5、第五行文件的二进制数据 + "\r\n"
:
这里跟普通表单提交一样;
结尾行:"--" + boundary + "--" + "\r\n"
可以看到,文件上传的诗句格式跟我们上一篇博文中讲到的表单提交只有两个地方不同,1、第二行的时候增加了一个文件名变量,2、增加了一行Content-Type: 文件的 mime 类型 + "\r\n"
;
文件也可以同时上传多个文件,上传多个文件的时候重复1、2、3、4、5步,在最后的一个文件的末尾加上统一的结束行。
文件实体类
这里是对图片操作所以我建了一个FormImg.java
<code class="hljs java has-numbering"><span class="hljs-javadoc">/** * Created by moon.zhong on 2015/3/3. */</span> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FormImage</span> {</span> <span class="hljs-comment">//参数的名称</span> <span class="hljs-keyword">private</span> String mName ; <span class="hljs-comment">//文件名</span> <span class="hljs-keyword">private</span> String mFileName ; <span class="hljs-comment">//文件的 mime,需要根据文档查询</span> <span class="hljs-keyword">private</span> String mMime ; <span class="hljs-comment">//需要上传的图片资源,因为这里测试为了方便起见,直接把 bigmap 传进来,真正在项目中一般不会这般做,而是把图片的路径传过来,在这里对图片进行二进制转换</span> <span class="hljs-keyword">private</span> Bitmap mBitmap ; <span class="hljs-keyword">public</span> <span class="hljs-title">FormImage</span>(Bitmap mBitmap) { <span class="hljs-keyword">this</span>.mBitmap = mBitmap; } <span class="hljs-keyword">public</span> String <span class="hljs-title">getName</span>() { <span class="hljs-comment">// return mName;</span> <span class="hljs-comment">//测试,把参数名称写死</span> <span class="hljs-keyword">return</span> <span class="hljs-string">"uploadimg"</span> ; } <span class="hljs-keyword">public</span> String <span class="hljs-title">getFileName</span>() { <span class="hljs-comment">//测试,直接写死文件的名字</span> <span class="hljs-keyword">return</span> <span class="hljs-string">"test.png"</span>; } <span class="hljs-comment">//对图片进行二进制转换</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">byte</span>[] <span class="hljs-title">getValue</span>() { ByteArrayOutputStream bos = <span class="hljs-keyword">new</span> ByteArrayOutputStream() ; mBitmap.compress(Bitmap.CompressFormat.JPEG,<span class="hljs-number">80</span>,bos) ; <span class="hljs-keyword">return</span> bos.toByteArray(); } <span class="hljs-comment">//因为我知道是 png 文件,所以直接根据文档查的</span> <span class="hljs-keyword">public</span> String <span class="hljs-title">getMime</span>() { <span class="hljs-keyword">return</span> <span class="hljs-string">"image/png"</span>; } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li></ul>
Volley 对文件数据的封装
<code class="hljs scala has-numbering"><span class="hljs-javadoc">/** * Created by gyzhong on 15/3/1. */</span> public <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostUploadRequest</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Request</span><<span class="hljs-title">String</span>> {</span> <span class="hljs-javadoc">/** * 正确数据的时候回掉用 */</span> <span class="hljs-keyword">private</span> ResponseListener mListener ; <span class="hljs-comment">/*请求 数据通过参数的形式传入*/</span> <span class="hljs-keyword">private</span> List<FormImage> mListItem ; <span class="hljs-keyword">private</span> String BOUNDARY = <span class="hljs-string">"--------------520-13-14"</span>; <span class="hljs-comment">//数据分隔线</span> <span class="hljs-keyword">private</span> String MULTIPART_FORM_DATA = <span class="hljs-string">"multipart/form-data"</span>; public PostUploadRequest(String url, List<FormImage> listItem, ResponseListener listener) { <span class="hljs-keyword">super</span>(Method.POST, url, listener); <span class="hljs-keyword">this</span>.mListener = listener ; setShouldCache(<span class="hljs-keyword">false</span>); mListItem = listItem ; <span class="hljs-comment">//设置请求的响应事件,因为文件上传需要较长的时间,所以在这里加大了,设为5秒</span> setRetryPolicy(<span class="hljs-keyword">new</span> DefaultRetryPolicy(<span class="hljs-number">5000</span>,DefaultRetryPolicy.DEFAULT_MAX_RETRIES,DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)); } <span class="hljs-javadoc">/** * 这里开始解析数据 * <span class="hljs-javadoctag">@param</span> response Response from the network * <span class="hljs-javadoctag">@return</span> */</span> <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">protected</span> Response<String> parseNetworkResponse(NetworkResponse response) { <span class="hljs-keyword">try</span> { String mString = <span class="hljs-keyword">new</span> String(response.data, HttpHeaderParser.parseCharset(response.headers)); Log.v(<span class="hljs-string">"zgy"</span>, <span class="hljs-string">"====mString==="</span> + mString); <span class="hljs-keyword">return</span> Response.success(mString, HttpHeaderParser.parseCacheHeaders(response)); } <span class="hljs-keyword">catch</span> (UnsupportedEncodingException e) { <span class="hljs-keyword">return</span> Response.error(<span class="hljs-keyword">new</span> ParseError(e)); } } <span class="hljs-javadoc">/** * 回调正确的数据 * <span class="hljs-javadoctag">@param</span> response The parsed response returned by */</span> <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">protected</span> void deliverResponse(String response) { mListener.onResponse(response); } <span class="hljs-annotation">@Override</span> public byte[] getBody() <span class="hljs-keyword">throws</span> AuthFailureError { <span class="hljs-keyword">if</span> (mListItem == <span class="hljs-keyword">null</span>||mListItem.size() == <span class="hljs-number">0</span>){ <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.getBody() ; } ByteArrayOutputStream bos = <span class="hljs-keyword">new</span> ByteArrayOutputStream() ; int N = mListItem.size() ; FormImage formImage ; <span class="hljs-keyword">for</span> (int i = <span class="hljs-number">0</span>; i < N ;i++){ formImage = mListItem.get(i) ; StringBuffer sb= <span class="hljs-keyword">new</span> StringBuffer() ; <span class="hljs-comment">/*第一行*/</span> <span class="hljs-comment">//`"--" + BOUNDARY + "\r\n"`</span> sb.append(<span class="hljs-string">"--"</span>+BOUNDARY); sb.append(<span class="hljs-string">"\r\n"</span>) ; <span class="hljs-comment">/*第二行*/</span> <span class="hljs-comment">//Content-Disposition: form-data; name="参数的名称"; filename="上传的文件名" + "\r\n"</span> sb.append(<span class="hljs-string">"Content-Disposition: form-data;"</span>); sb.append(<span class="hljs-string">" name=\""</span>); sb.append(formImage.getName()) ; sb.append(<span class="hljs-string">"\""</span>) ; sb.append(<span class="hljs-string">"; filename=\""</span>) ; sb.append(formImage.getFileName()) ; sb.append(<span class="hljs-string">"\""</span>); sb.append(<span class="hljs-string">"\r\n"</span>) ; <span class="hljs-comment">/*第三行*/</span> <span class="hljs-comment">//Content-Type: 文件的 mime 类型 + "\r\n"</span> sb.append(<span class="hljs-string">"Content-Type: "</span>); sb.append(formImage.getMime()) ; sb.append(<span class="hljs-string">"\r\n"</span>) ; <span class="hljs-comment">/*第四行*/</span> <span class="hljs-comment">//"\r\n"</span> sb.append(<span class="hljs-string">"\r\n"</span>) ; <span class="hljs-keyword">try</span> { bos.write(sb.toString().getBytes(<span class="hljs-string">"utf-8"</span>)); <span class="hljs-comment">/*第五行*/</span> <span class="hljs-comment">//文件的二进制数据 + "\r\n"</span> bos.write(formImage.getValue()); bos.write(<span class="hljs-string">"\r\n"</span>.getBytes(<span class="hljs-string">"utf-8"</span>)); } <span class="hljs-keyword">catch</span> (IOException e) { e.printStackTrace(); } } <span class="hljs-comment">/*结尾行*/</span> <span class="hljs-comment">//`"--" + BOUNDARY + "--" + "\r\n"`</span> String endLine = <span class="hljs-string">"--"</span> + BOUNDARY + <span class="hljs-string">"--"</span> + <span class="hljs-string">"\r\n"</span> ; <span class="hljs-keyword">try</span> { bos.write(endLine.toString().getBytes(<span class="hljs-string">"utf-8"</span>)); } <span class="hljs-keyword">catch</span> (IOException e) { e.printStackTrace(); } Log.v(<span class="hljs-string">"zgy"</span>,<span class="hljs-string">"=====formImage====\n"</span>+bos.toString()) ; <span class="hljs-keyword">return</span> bos.toByteArray(); } <span class="hljs-comment">//Content-Type: multipart/form-data; boundary=----------8888888888888</span> <span class="hljs-annotation">@Override</span> public String getBodyContentType() { <span class="hljs-keyword">return</span> MULTIPART_FORM_DATA+<span class="hljs-string">"; boundary="</span>+BOUNDARY; } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li><li>89</li><li>90</li><li>91</li><li>92</li><li>93</li><li>94</li><li>95</li><li>96</li><li>97</li><li>98</li><li>99</li><li>100</li><li>101</li><li>102</li><li>103</li><li>104</li><li>105</li><li>106</li><li>107</li><li>108</li><li>109</li><li>110</li><li>111</li><li>112</li><li>113</li></ul>
因为代码中注解写的比较详细,加上很多东西在前面几篇 blog 已经讲过了,所以这里直接上代码。
文件上传接口
<code class="hljs java has-numbering"><span class="hljs-javadoc">/** * Created by moon.zhong on 2015/3/3. */</span> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UploadApi</span> {</span> <span class="hljs-javadoc">/** * 上传图片接口 *<span class="hljs-javadoctag"> @param</span> bitmap 需要上传的图片 *<span class="hljs-javadoctag"> @param</span> listener 请求回调 */</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">uploadImg</span>(Bitmap bitmap,ResponseListener listener){ List<FormImage> imageList = <span class="hljs-keyword">new</span> ArrayList<FormImage>() ; imageList.add(<span class="hljs-keyword">new</span> FormImage(bitmap)) ; Request request = <span class="hljs-keyword">new</span> PostUploadRequest(Constant.UploadHost,imageList,listener) ; VolleyUtil.getRequestQueue().add(request) ; } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li></ul>
图片上传验证
上传类PostUploadActivity.java
<code class="hljs java has-numbering"><span class="hljs-javadoc">/** * Created by moon.zhong on 2015/3/2. */</span> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostUploadActivity</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ActionBarActivity</span> {</span> <span class="hljs-keyword">private</span> TextView mShowResponse ; <span class="hljs-keyword">private</span> ImageView mImageView ; <span class="hljs-keyword">private</span> ProgressDialog mDialog ; <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(Bundle savedInstanceState) { <span class="hljs-keyword">super</span>.onCreate(savedInstanceState); setContentView(R.layout.activity_upload_img); mShowResponse = (TextView) findViewById(R.id.id_show_response) ; mImageView = (ImageView) findViewById(R.id.id_show_img) ; mDialog = <span class="hljs-keyword">new</span> ProgressDialog(<span class="hljs-keyword">this</span>) ; mDialog.setCanceledOnTouchOutside(<span class="hljs-keyword">false</span>); } <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">uploadImg</span>(View view){ mDialog.setMessage(<span class="hljs-string">"图片上传中..."</span>); mDialog.show(); Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.logo) ; UploadApi.uploadImg(bitmap,<span class="hljs-keyword">new</span> ResponseListener<String>() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onErrorResponse</span>(VolleyError error) { Log.v(<span class="hljs-string">"zgy"</span>,<span class="hljs-string">"===========VolleyError========="</span>+error) ; mShowResponse.setText(<span class="hljs-string">"ErrorResponse\n"</span>+error.getMessage()); Toast.makeText(PostUploadActivity.<span class="hljs-keyword">this</span>,<span class="hljs-string">"上传失败"</span>,Toast.LENGTH_SHORT).show() ; mDialog.dismiss(); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onResponse</span>(String response) { response = response.substring(response.indexOf(<span class="hljs-string">"img src="</span>)); response = response.substring(<span class="hljs-number">8</span>,response.indexOf(<span class="hljs-string">"/>"</span>)) ; Log.v(<span class="hljs-string">"zgy"</span>,<span class="hljs-string">"===========onResponse========="</span>+response) ; mShowResponse.setText(<span class="hljs-string">"图片地址:\n"</span>+response); mDialog.dismiss(); Toast.makeText(PostUploadActivity.<span class="hljs-keyword">this</span>,<span class="hljs-string">"上传成功"</span>,Toast.LENGTH_SHORT).show(); } }) ; } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li></ul>
测试结果如下:
上传图片页面:
图片上传中
图片上传成功,地址为http://www.chuantu.biz/t/67/1425474351x-1376440163.png
通过网页请求
可以看到,volley 实现文件上传的操作还是很方便的,不过,不知道大家看到这里有没有觉得哪里有问题呢?其实 volley 实现文件上传是有一个很大的问题,什么问题呢,大家自己先想想,我将会在后续的文章中讲到这个问题,并提供解决方案(是后续,不是下一篇)。volley 讲到这里为止,对于它的功能也讲了一大部分,不过还有一个非常有用的知识点没有讲到,那就是volley缓存机制,下一节,将开启 volley 的缓存之旅,敬请期待!
转载自:http://blog.youkuaiyun.com/jxxfzgy/article/details/44064481#reply