Android中利用volley同时上传文件和文本参数

Android中利用volley同时上传文件和文本参数

前言

在做项目时利用了Volley网络库与服务器之间进行通信,但Volley并没有现成的上传文件的Request可以调用,网上搜索一圈发现很多都是基于HttpClient进行自定义Request封装,可是无奈android 6.0之后移除了HttpClient的代码,只能自己基于HttpURLConnection封装一个Request使用。

继承Request,重写一些方法

由于我的项目中服务器返回的结果都是json格式,所以我自定义一个返回结果是json格式的request,你也可以换成String格式,或者其他你需要的格式。

public class PostUploadRequest extends Request<JSONObject>{

public PostUploadRequest(String url, Map<String, String[]> fileMap, Response.Listener<JSONObject> mListener, Response.ErrorListener listener) {
        super(Method.POST, url, listener);
        this.mListener = mListener;
        this.fileMap = fileMap;
        setShouldCache(false);
        setRetryPolicy(new DefaultRetryPolicy(5000, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
    }


}

重写parseNetworkResponse()方法

将返回结果转换成json格式

try {
   String je = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
   return Response.success(new JSONObject(je), HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException var3) {
    return Response.error(new ParseError(var3));
} catch (JSONException var4) {
    return Response.error(new ParseError(var4));
}

重写deliverResponse()方法

为请求添加监听回调事件

@Override
    protected void deliverResponse(JSONObject jsonObject) {
        mListener.onResponse(jsonObject);
    }

模仿浏览器上传文件请求

private void buildFilePart(DataOutputStream ds) {
        StringBuilder sb = new StringBuilder();
        Object[] key_arr = fileMap.keySet().toArray();
        for (Object name : key_arr) {
            String[] val = fileMap.get(name.toString());
            String path = val[0];
            String filename = val[1];
            try {
                sb.append(prefix);
                sb.append(boundary);
                sb.append(end);
                sb.append("Content-Disposition: form-data;name=\"");
                sb.append(name.toString());
                sb.append("\";filename=\"");
                sb.append(filename);
                sb.append("\"" + end);//filename是文件名,如xxx.jpg,file是服务器传递参数的名字
                sb.append("Content-Type: application/octet-stream;charset=UTF-8" + end);
                sb.append(end);
                ds.write(sb.toString().getBytes("UTF-8"));
                /* 取得文件的FileInputStream */
                FileInputStream fStream = new FileInputStream(path);//path是文件本地地址
                /* 设置每次写入1024bytes */
                int bufferSize = 1024;
                byte[] buffer = new byte[bufferSize];
                int length;
                /* 从文件读取数据至缓冲区 */
                while ((length = fStream.read(buffer)) != -1) {
                    /* 将资料写入DataOutputStream中 */
                    ds.write(buffer, 0, length);
                }
                ds.write(end.getBytes("UTF-8"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

模仿浏览器传递普通参数请求

private void buildTextPart(DataOutputStream ds) {
        try {
            Map<String, String> map = getParams();
            Object[] key_arr = map.keySet().toArray();
            for (Object name : key_arr) {
                String val = map.get(name.toString());
                ds.write((prefix + boundary + end).getBytes("UTF-8"));
                ds.write(("Content-Disposition: form-data;name=\"" + name.toString() + "\"" + end).getBytes("UTF-8"));
                ds.write(("Content-Type: text/plain;charset=UTF-8" + end).getBytes("UTF-8"));
                ds.write((end).getBytes("UTF-8"));
                ds.write((val + end).getBytes("UTF-8"));
            }
        } catch (IOException | AuthFailureError e) {
            e.printStackTrace();
        }
    }

重写getBody()方法

@Override
    public byte[] getBody() throws AuthFailureError {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            DataOutputStream ds = new DataOutputStream(bos);
            buildTextPart(ds);
            buildFilePart(ds);
            ds.write((prefix + boundary + prefix + end).getBytes("UTF-8"));
            ds.flush();
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

使用

和正常使用volley提供的request一样使用,不同的是构造方法中传递参数有略微不同,传递参数中Map<String,String[]> fileMap
key存放的是参数名
value是长度为2的字符串数组, 下标0存放的是文件的本地路径,下标1存放的是传送到服务器中的文件名,通过fileMap可以一次性上传多个参数,如果需要同时上传普通文本参数,可以在构造request是重写getParams()方法以Map<String,String> 形式传递多个文本参数和参数值,类似StringRequest. 示例代码如下

Response.Listener<JSONObject> listener = new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject jsonObject) {
                //请求成功后的处理
            }
        };
        Response.ErrorListener errorListener = new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                //请求失败后的处理
        };
        PostUploadRequest request = new PostUploadRequest(url, fileMap, listener, errorListener) {
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                return map;//map存放的是普通参数
            }
        };
        //RequestQueue.add(request)
        //发起请求

以下是request的完整代码

public class PostUploadRequest extends Request<JSONObject> {

    private final String prefix = "--";
    private final String end = "\r\n";
    private final String boundary = "--------------" + System.currentTimeMillis();
    private final String mimeType = "multipart/form-data;boundary=" + boundary;
    private Response.Listener<JSONObject> mListener;
    private Map<String, String[]> fileMap;

    public PostUploadRequest(String url, Map<String, String[]> fileMap, Response.Listener<JSONObject> mListener, Response.ErrorListener listener) {
        super(Method.POST, url, listener);
        this.mListener = mListener;
        this.fileMap = fileMap;
        setShouldCache(false);
        setRetryPolicy(new DefaultRetryPolicy(5000, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
    }

    @Override
    protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
        try {
            String je = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            return Response.success(new JSONObject(je), HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException var3) {
            return Response.error(new ParseError(var3));
        } catch (JSONException var4) {
            return Response.error(new ParseError(var4));
        }
    }

    @Override
    protected void deliverResponse(JSONObject jsonObject) {
        mListener.onResponse(jsonObject);
    }

    @Override
    public String getBodyContentType() {
        return mimeType;
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            DataOutputStream ds = new DataOutputStream(bos);
            buildTextPart(ds);
            buildFilePart(ds);
            ds.write((prefix + boundary + prefix + end).getBytes("UTF-8"));
            ds.flush();
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void buildFilePart(DataOutputStream ds) {
        StringBuilder sb = new StringBuilder();
        Object[] key_arr = fileMap.keySet().toArray();
        for (Object name : key_arr) {
            String[] val = fileMap.get(name.toString());
            String path = val[0];
            String filename = val[1];
            try {
                sb.append(prefix);
                sb.append(boundary);
                sb.append(end);
                sb.append("Content-Disposition: form-data;name=\"");
                sb.append(name.toString());
                sb.append("\";filename=\"");
                sb.append(filename);
                sb.append("\"" + end);//filename是文件名,如xxx.jpg,file是服务器传递参数的名字
                sb.append("Content-Type: application/octet-stream;charset=UTF-8" + end);
                sb.append(end);
                ds.write(sb.toString().getBytes("UTF-8"));
                /* 取得文件的FileInputStream */
                FileInputStream fStream = new FileInputStream(path);//path是文件本地地址
                /* 设置每次写入1024bytes */
                int bufferSize = 1024;
                byte[] buffer = new byte[bufferSize];
                int length;
                /* 从文件读取数据至缓冲区 */
                while ((length = fStream.read(buffer)) != -1) {
                    /* 将资料写入DataOutputStream中 */
                    ds.write(buffer, 0, length);
                }
                ds.write(end.getBytes("UTF-8"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void buildTextPart(DataOutputStream ds) {
        try {
            Map<String, String> map = getParams();
            Object[] key_arr = map.keySet().toArray();
            for (Object name : key_arr) {
                String val = map.get(name.toString());
                ds.write((prefix + boundary + end).getBytes("UTF-8"));
                ds.write(("Content-Disposition: form-data;name=\"" + name.toString() + "\"" + end).getBytes("UTF-8"));
                ds.write(("Content-Type: text/plain;charset=UTF-8" + end).getBytes("UTF-8"));
                ds.write((end).getBytes("UTF-8"));
                ds.write((val + end).getBytes("UTF-8"));
            }
        } catch (IOException | AuthFailureError e) {
            e.printStackTrace();
        }
    }

}

一些话

  1. 第一次写博客,可能格式内容上比较粗糙,欢迎各位批评指正
  2. 代码上的实现主要是为了手上项目,通用性上可能会有所欠缺,如果使用过程中出现bug或者有一些疑问,欢迎评论指出,或者联系我的邮箱:13813005628@163.com
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值