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();
}
}
}
一些话
- 第一次写博客,可能格式内容上比较粗糙,欢迎各位批评指正
- 代码上的实现主要是为了手上项目,通用性上可能会有所欠缺,如果使用过程中出现bug或者有一些疑问,欢迎评论指出,或者联系我的邮箱:13813005628@163.com