Volley框架(四):使用Volley上传文件
来源:互联网 发布:rpc java 编辑:程序博客网 时间:2024/05/01 12:50
在上一篇博客《Volley框架(三):使用Volley提交表单数据》中,我们已经知道了使用Volley提交表单数据,这篇博客我们来说一下使用Volley实现上传文件。
首先,我们还是一样,通过网络抓包看看上传文件的格式是什么,与提交表单数据有什么不同。
POST http://www.aaa.com/?ashx/login.ashx HTTP/1.1Host: www.aaa.comUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3Accept-Encoding: gzip, deflateConnection: keep-aliveUpgrade-Insecure-Requests: 1Content-Type: multipart/form-data; boundary=---------------------------29518470621122Content-Length: 6462-----------------------------29518470621122Content-Disposition: form-data; name="login_username"abcde-----------------------------29518470621122Content-Disposition: form-data; name="login_pswd"12345-----------------------------29518470621122Content-Disposition: form-data; name="upfile"; filename="error_img.jpg"Content-Type: image/jpeg?? JFIF ? C $.' ",#(7),01444'9=82<.342? C2!!22222222222222222222222222222222222222222222222222? -----------------------------29518470621122--如果看了上一篇博客的分析,那么在这里我们很容易就知道先是提交的2条文字数据,然后这里多了一段,和提交文字数据的格式类似,只有部分不同的数据;没错,这里多的这一段就是上传文件的格式,乱码部分就是文件的二进制数据(算做一行),然后最后有一个结束符,和提交表单数据一样。
下面我们分析一下提交文件的格式:
第一行:"--" + boundary + "\r\n"说明:”–”为数据开始标志,boundary为http实体头定义的边界分割线,boundary可以是任意的字符串,只要和Content-Type: multipart/form-data; boundary=----29518470621122中保持一直即可,”\r\n”为回车换行;
第二行:Content-Disposition: form-data; name="参数的名称"; filename="上传的文件名" + "\r\n"说明:这里比普通的表单多了一个filename=”上传的文件名”;
第三行:Content-Type: 文件的 mime 类型 + "\r\n"说明:这一行是文件上传必须要的,而普通的文字提交可有可无,mime 类型需要根据文档查询;
第四行:\r\n说明:只有换行符;
第五行:文件的二进制数据 + "\r\n"说明:文件的二进制数据加上换行符。
结束标志:"--" + boundary + "--" + "\r\n"说明:在所有的数据结束之后,需要有这个结尾标志。
文件也可以同时上传多个文件,上传多个文件的时候重复1、2、3、4、5步,在最后的一个文件的末尾加上统一的结束行。
通过上面对格式的分析,我们发现文件上传和表单提交数据一样,所以我们也使用同样的方法实现文件上传,只需自定义一个Request就可以了,同样也需要重写两个方法:
① 需要重写获取实体的方法
public byte[] getBody() throws AuthFailureError{}② 在提交表单数的时候需要在 http 头部中声明内容类型为表单数据
重写下面的方法,返回声明的内容数据类型
public String getBodyContentType() {// multipart/form-data; boundary=----WebKitFormBoundarykR96Kta4gvMACHfqreturn MULTIPART_FORM_DATA + ";boundary=" + BOUNDARY;}自定义Request在《Volley框架(一):使用Volley请求数据》这篇博客中有说到。
自定义文件上传实体:
public class FileEntity { /** * 参数名称 */ public String mName; /** * 上传的文件名 */ public String mFileName; /** * 需要上传的文件 */ public File mFile; /** * 文件的 mime,需要根据文档查询<br/> * 默认使用 application/octet-stream 任意的二进制数据 */ public String mMime = "application/octet-stream"; public FileEntity() { } public FileEntity(String mName, String mFileName, File mFile, String mMime) { this.mName = mName; this.mFileName = mFileName; this.mFile = mFile; this.mMime = mMime; } public byte[] getFileBytes() { FileInputStream fileInputStream = null; ByteArrayOutputStream outputStream = null; try { fileInputStream = new FileInputStream(mFile); outputStream = new ByteArrayOutputStream(); int len; byte[] bytes = new byte[1024]; while ((len = fileInputStream.read(bytes)) != -1){ outputStream.write(bytes,0,len); } return outputStream.toByteArray(); } catch (IOException e) { e.printStackTrace(); }finally{ if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; }}
自定义上传文件的Request:
public class MultipartRequest extends Request<String> { private final String MULTIPART_FORM_DATA = "multipart/form-data"; // 数据类型 private final String BOUNDARY = "---------" + UUID.randomUUID().toString(); // 随机生成边界分隔线 private final String NEW_LINE = "\r\n"; // 换行符 private Map<String, Object> mParams; private List<FileEntity> mFileEntityList; private FileEntity mFileEntity; private Response.Listener<String> mListener; private Charset mCharSet = Charset.defaultCharset(); private boolean isSingleFile = false; public MultipartRequest(String url, Map<String, Object> params, Charset charSet, List<FileEntity> fileEntityList, Response.Listener<String> listener, Response.ErrorListener errorListener) { super(Method.POST, url, errorListener); this.mParams = params; this.mCharSet = charSet; this.mFileEntityList = fileEntityList; this.mListener = listener; this.isSingleFile = false; } public MultipartRequest(String url, Map<String, Object> params, Charset charSet, FileEntity fileEntity, Response.Listener<String> listener, Response.ErrorListener errorListener) { super(Method.POST, url, errorListener); this.mParams = params; this.mCharSet = charSet; this.mFileEntity = fileEntity; this.mListener = listener; this.isSingleFile = true; } public MultipartRequest(String url, Map<String, Object> params, List<FileEntity> fileEntityList, Response.Listener<String> listener, Response.ErrorListener errorListener) { this(url, params, Charset.defaultCharset(), fileEntityList, listener, errorListener); } public MultipartRequest(String url, Map<String, Object> params, FileEntity fileEntity, Response.Listener<String> listener, Response.ErrorListener errorListener) { this(url, params, Charset.defaultCharset(), fileEntity, listener, errorListener); } @Override protected Response<String> parseNetworkResponse(NetworkResponse response) { try { Log.i("MultipartRequest", new String(response.data, "utf-8")); return Response.success(new String(response.data, HttpHeaderParser.parseCharset(response.headers)), HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return Response.error(new ParseError(e)); } } @Override protected void deliverResponse(String response) { mListener.onResponse(response); } @Override public byte[] getBody() throws AuthFailureError { if (isSingleFile) { // 单文件上传 return singleFileUp(); } else { // 多个文件上传 return multipleFileUp(); } } /** * 多个文件上传 * * @return * @throws AuthFailureError */ private byte[] multipleFileUp() throws AuthFailureError { if ((mParams == null || mParams.size() <= 0) && (mFileEntityList == null || mFileEntityList.size() <= 0)) { // 没有参数也没有文件 return super.getBody(); } else { ByteArrayOutputStream bos = new ByteArrayOutputStream(); if (mParams != null && mParams.size() > 0) { // 有参数,先拼接参数 paramsFormat(bos); } if (mFileEntityList != null && mFileEntityList.size() > 0) { // 有文件,提交文件 for (FileEntity fileEntity : mFileEntityList) { fileFormat(bos, fileEntity); } } // 所有参数拼接完成,拼接结束行 endLine(bos); return bos.toByteArray(); } } /** * 单个文件上传 * * @return * @throws AuthFailureError */ private byte[] singleFileUp() throws AuthFailureError { if ((mParams == null || mParams.size() <= 0) && (mFileEntity == null)) { // 没有参数也没有文件 return super.getBody(); } else { ByteArrayOutputStream bos = new ByteArrayOutputStream(); if (mParams != null && mParams.size() > 0) { // 有参数,先拼接参数 paramsFormat(bos); } if (mFileEntity != null) { // 有文件,提交文件 fileFormat(bos, mFileEntity); } // 所有参数拼接完成,拼接结束行 endLine(bos); return bos.toByteArray(); } } /** * 结束行内容 * * @param bos */ private void endLine(ByteArrayOutputStream bos) { String endLine = "--" + BOUNDARY + "--" + NEW_LINE; try { bos.write(endLine.getBytes(mCharSet)); } catch (IOException e) { e.printStackTrace(); } } /** * 格式化上传文件格式 * * @param bos * @param fileEntity */ private void fileFormat(ByteArrayOutputStream bos, FileEntity fileEntity) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("--").append(BOUNDARY).append(NEW_LINE); stringBuilder.append("Content-Disposition: form-data; name=\"").append(fileEntity.mName).append("\"").append(";filename=\"").append(fileEntity.mFileName).append("\"").append(NEW_LINE); stringBuilder.append("Content-Type: ").append(fileEntity.mMime).append(";charset=").append(mCharSet).append(NEW_LINE); stringBuilder.append(NEW_LINE); try { bos.write(stringBuilder.toString().getBytes(mCharSet)); bos.write(fileEntity.getFileBytes()); bos.write(NEW_LINE.getBytes(mCharSet)); } catch (IOException e) { e.printStackTrace(); } } /** * 格式化上传参数格式 * * @param bos */ private void paramsFormat(ByteArrayOutputStream bos) { StringBuilder stringBuilder = new StringBuilder(); for (String key : mParams.keySet()) { Object value = mParams.get(key); stringBuilder.append("--").append(BOUNDARY).append(NEW_LINE); stringBuilder.append("Content-Disposition: form-data; name=\"").append(key).append("\"").append(NEW_LINE); stringBuilder.append(NEW_LINE); stringBuilder.append(value).append(NEW_LINE); } try { bos.write(stringBuilder.toString().getBytes(mCharSet)); } catch (IOException e) { e.printStackTrace(); } } /** * 返回头信息,用于指定上传的内容类型 * * @return */ @Override public String getBodyContentType() { // 如果参数和文件都为null时,不会执行这个方法 // Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryS4nmHw9nb2Eeusll return MULTIPART_FORM_DATA + ";boundary=" + BOUNDARY; }}自定义Request写完了,我们接下来测试一下效果,在这里我就使用自己写的一个简单的servlet来测试(只贴出上传单个文件的代码,所有的代码已上传到这里,点击下载):
File directory = Environment.getExternalStorageDirectory();// 参数Map<String, Object> params = new HashMap<>();params.put("name", "volley_single_file_name");params.put("value", "volley_single_file_value");FileEntity fileEntity = new FileEntity();fileEntity.mName = "filename";fileEntity.mFileName = "other.png";fileEntity.mFile = new File(directory, "Movies/other.png");RequestQueue requestQueue = Volley.newRequestQueue(this);String url = "http://192.168.0.100:8080/UpLoadFile/upload";MultipartRequest multipartRequest = new MultipartRequest(url, params, fileEntity, new Response.Listener<String>() {@Overridepublic void onResponse(String response) {Log.e("Succeed Result", response);}}, new Response.ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {Log.e("Error Result", error + "");}});requestQueue.add(multipartRequest);运行结果查看(查看后台打印结果):
这样,我们就实现了使用Volley上传文件。同样我们可以进一步将代码封装,减少重复的代码。
点击下载全部封装代码。
下一篇《Volley框架(五):Volley源码分析》
0 0
- Volley框架(四):使用Volley上传文件
- Android使用Volley上传文件
- Volley 网络框架实现文件上传
- Volley框架上传文件TimeoutError问题
- Volley上传文件
- volley文件上传
- Volley上传文件
- Volley使用(四)
- Volley框架的使用
- Android Volley框架使用
- Volley框架的使用
- 为什么使用Volley框架
- Volley框架的使用
- Volley框架的使用
- Volley框架的使用
- Volley框架的使用
- Volley框架的使用
- Volley框架的使用
- Vulkan编程指南翻译 第六章 着色器和管线 第3节 管线
- set_task_state和set_current_state
- 【每天一个linux命令】chattr
- 关于路由相关
- JAVA源码解析-String源码
- Volley框架(四):使用Volley上传文件
- 云计算详解|狭义云计算与广义云计算|云计算到底是什么呢?判断云计算的标准是什么?
- Androidstudio升级后Refreshing Gradle Project编译更新Gradle卡住问题
- [OpenGL学习] mac上运行第一个openGL程序
- mongodb
- 《邓小平文选》与电子版《邓选》配套同步全国发行开创马克思主义经典著电子化作新时代
- 像jQuery那样封装一个ajax
- Scala入门01
- PySide程序的国际化