android文件上传
来源:互联网 发布:有淘宝店铺可以直播吗 编辑:程序博客网 时间:2024/05/16 18:37
一 概述:
1.这篇文章是基于volley框架,新加的文件上传的代码的分析
2.主要是了解这个请求类:MultiPartRequest
3.我下的这个volley库,上传时候有点小问题.
二 认识MultiPartRequest:
1.先贴源码
private UploadMultipartEntity mMultipartEntity;
/**
* Default connection timeout for Multipart requests
*/
public static final int TIMEOUT_MS = 60000;
public MultiPartRequest(int method, String url, Listener<T> listener, ErrorListener errorlistener, LoadingListener loadingListener) {
super(method, url, errorlistener);
mListener = listener;
mMultipartEntity = new UploadMultipartEntity();
final ExecutorDelivery delivery = new ExecutorDelivery(new Handler(Looper.getMainLooper()));
setLoadingListener(loadingListener);
if (loadingListener != null) {
mMultipartEntity.setListener(new ProgressListener() {
long time = SystemClock.uptimeMillis();
long count = -1;
@Override
public void transferred(long num) {
if (count == -1) {
count = mMultipartEntity.getContentLength();
}
// LogUtils.d("bacy", "upload->" + count + ",num->" + num);
long thisTime = SystemClock.uptimeMillis();
if (thisTime - time >= getRate() || count == num) {
time = thisTime;
delivery.postLoading(MultiPartRequest.this, count, num);
}
}
});
}
setRetryPolicy(new DefaultRetryPolicy(TIMEOUT_MS, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
}
@Override
public String getBodyContentType() {
return mMultipartEntity.getContentType().getValue();
}
@Override
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
/**
* Get the protocol charset
*/
public String getProtocolCharset() {
return PROTOCOL_CHARSET;
}
public void addPart(String key, String value) {
StringPart part = new StringPart(key, value, PROTOCOL_CHARSET);
mMultipartEntity.addPart(part);
}
public void addPart(String key, File file) {
FilePart part = new FilePart(key, file, null, null);
mMultipartEntity.addPart(part);
}
public UploadMultipartEntity getMultipartEntity() {
return mMultipartEntity;
}
/**
* Default connection timeout for Multipart requests
*/
public static final int TIMEOUT_MS = 60000;
public MultiPartRequest(int method, String url, Listener<T> listener, ErrorListener errorlistener, LoadingListener loadingListener) {
super(method, url, errorlistener);
mListener = listener;
mMultipartEntity = new UploadMultipartEntity();
final ExecutorDelivery delivery = new ExecutorDelivery(new Handler(Looper.getMainLooper()));
setLoadingListener(loadingListener);
if (loadingListener != null) {
mMultipartEntity.setListener(new ProgressListener() {
long time = SystemClock.uptimeMillis();
long count = -1;
@Override
public void transferred(long num) {
if (count == -1) {
count = mMultipartEntity.getContentLength();
}
// LogUtils.d("bacy", "upload->" + count + ",num->" + num);
long thisTime = SystemClock.uptimeMillis();
if (thisTime - time >= getRate() || count == num) {
time = thisTime;
delivery.postLoading(MultiPartRequest.this, count, num);
}
}
});
}
setRetryPolicy(new DefaultRetryPolicy(TIMEOUT_MS, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
}
@Override
public String getBodyContentType() {
return mMultipartEntity.getContentType().getValue();
}
@Override
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
/**
* Get the protocol charset
*/
public String getProtocolCharset() {
return PROTOCOL_CHARSET;
}
public void addPart(String key, String value) {
StringPart part = new StringPart(key, value, PROTOCOL_CHARSET);
mMultipartEntity.addPart(part);
}
public void addPart(String key, File file) {
FilePart part = new FilePart(key, file, null, null);
mMultipartEntity.addPart(part);
}
public UploadMultipartEntity getMultipartEntity() {
return mMultipartEntity;
}
在这个类中,定义了UploadMultipartEntity这个类是继承于MultipartEntity,只要把要上传的文件通过方法addPart加入就ok.为什么呢?
1.首先了解volley库的整个运作流程:
三:先分析下整个流程:
在网络请求时候,会调用BasicNetwork的performRequest方法,源码如下(只截取了一部分):
public NetworkResponse performRequest(ResponseDelivery delivery, Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
try {
if (!URLUtil.isNetworkUrl(request.getUrl())) {
return new NetworkResponse(responseContents);
}
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
2.然后看这一行httpResponse = mHttpStack.performRequest(request, headers);
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
try {
if (!URLUtil.isNetworkUrl(request.getUrl())) {
return new NetworkResponse(responseContents);
}
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
2.然后看这一行httpResponse = mHttpStack.performRequest(request, headers);
responseHeaders = convertHeaders(httpResponse.getAllHeaders());调用了mHttpStack用于网络请求的类
在这个方法里面调用了 setConnectionParametersForRequest(connection, request);
3.然后在这个方法里面,假如是用POST请求,那么,执行到:
case Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
4.再看下addBodylfExists
private static void addBodyIfExists(HttpURLConnection connection, final Request<?> request)
throws IOException, AuthFailureError {
connection.setDoOutput(true);
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
if (request instanceof MultiPartRequest) {
final UploadMultipartEntity multipartEntity = ((MultiPartRequest<?>) request).getMultipartEntity();
// 防止所有文件写到内存中
connection.setFixedLengthStreamingMode((int)multipartEntity.getContentLength());
multipartEntity.writeTo(connection.getOutputStream());
} else {
byte[] body = request.getBody();
if (body != null) {
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
}
}
}
throws IOException, AuthFailureError {
connection.setDoOutput(true);
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
if (request instanceof MultiPartRequest) {
final UploadMultipartEntity multipartEntity = ((MultiPartRequest<?>) request).getMultipartEntity();
// 防止所有文件写到内存中
connection.setFixedLengthStreamingMode((int)multipartEntity.getContentLength());
multipartEntity.writeTo(connection.getOutputStream());
} else {
byte[] body = request.getBody();
if (body != null) {
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
}
}
}
在这里获取了MultiPartRequest的UploadMultipartEntity成员变量,然后调用了writeTo方法.
然后回过头来看这个类UploadMultipartEntity他的方法为:
@Override
public void writeTo(OutputStream outstream) throws IOException {
if (listener == null) {
super.writeTo(outstream);
} else {
super.writeTo(new CountingOutputStream(outstream, offset, this.listener));
}
}
public void writeTo(OutputStream outstream) throws IOException {
if (listener == null) {
super.writeTo(outstream);
} else {
super.writeTo(new CountingOutputStream(outstream, offset, this.listener));
}
}
这里判断一下是否传入了listener,这个什么用呢,其实是用来计算进度的,也就是文件上传的百分比
然后先看 super.writeTo(outstream);
public void writeTo(final OutputStream out) throws IOException {
if (out == null) {
throw new IllegalArgumentException("Output stream may not be null"); //$NON-NLS-1$
}
for (Part part : parts) {
part.writeTo(out, boundary);
}
out.write(boundary.getClosingBoundary());
out.flush();
}
if (out == null) {
throw new IllegalArgumentException("Output stream may not be null"); //$NON-NLS-1$
}
for (Part part : parts) {
part.writeTo(out, boundary);
}
out.write(boundary.getClosingBoundary());
out.flush();
}
在这里就把那些添加的Part依次调用Part的WriteTo直接写入输出流,然后看下我们写入的文件部分,也就是FilePart类
他的writeTo方法如下:
public void writeTo(OutputStream out, Boundary boundary) throws IOException {
out.write(getHeader(boundary));
InputStream in = new FileInputStream(file);
try {
byte[] tmp = new byte[4096];
int l;
while ((l = in.read(tmp)) != -1) {
out.write(tmp, 0, l);
}
} finally {
in.close();
}
out.write(CRLF);
}
out.write(getHeader(boundary));
InputStream in = new FileInputStream(file);
try {
byte[] tmp = new byte[4096];
int l;
while ((l = in.read(tmp)) != -1) {
out.write(tmp, 0, l);
}
} finally {
in.close();
}
out.write(CRLF);
}
看到这里,可能明白了,他就是最好按照文件的上传格式,先上传Boundary边界,然后再上传文件自己的流
四:自己找出的问题所在:
在获取这个边界的时候最后会运行到如下代码:
private byte[] generateHeader(Boundary boundary) {
if (headersProvider == null) {
throw new RuntimeException("Uninitialized headersProvider"); //$NON-NLS-1$
}
final ByteArrayBuffer buf = new ByteArrayBuffer(256);
append(buf, boundary.getStartingBoundary());
append(buf, headersProvider.getContentDisposition());
append(buf, CRLF);
append(buf, headersProvider.getContentType());
append(buf, CRLF);
append(buf, headersProvider.getContentTransferEncoding());
append(buf, CRLF);
append(buf, CRLF);
return buf.toByteArray();
}
if (headersProvider == null) {
throw new RuntimeException("Uninitialized headersProvider"); //$NON-NLS-1$
}
final ByteArrayBuffer buf = new ByteArrayBuffer(256);
append(buf, boundary.getStartingBoundary());
append(buf, headersProvider.getContentDisposition());
append(buf, CRLF);
append(buf, headersProvider.getContentType());
append(buf, CRLF);
append(buf, headersProvider.getContentTransferEncoding());
append(buf, CRLF);
append(buf, CRLF);
return buf.toByteArray();
}
在服务器根本就不解释这一行,也即是append(buf, headersProvider.getContentTransferEncoding());
append(buf, CRLF);,所以服务器解释流的地方就不对,文件就打不开,所以把这2句代码屏蔽掉就行了!!!(研究了半天。。。)
append(buf, CRLF);,所以服务器解释流的地方就不对,文件就打不开,所以把这2句代码屏蔽掉就行了!!!(研究了半天。。。)
五:结束语:
先就写到这,那个用于进度的listener,相信聪明的大家一看就会。水平有限,但是努力追求技术的心是不会变的,不但要知其然,而且要知其所以然。顺便把volley上传下。。
1 0
- android http上传文件
- Android 上传大文件
- Android 上传大文件
- android 文件上传
- android 上传文件类
- android上传文件
- Android 文件上传参考
- Android 文件的上传
- Android入门:文件上传
- android之文件上传
- Android 上传文件
- Android上传文件,续
- android httpclient 上传文件
- android 文件上传
- Android中的文件上传
- android 文件上传
- Android文件上传
- Android文件上传
- 【poj1985】Cow Marathon 树形DP求树的直径
- 月薪3万的程序员都避开了哪些坑
- viewDidLoad和initWithNibName的具体含义
- U盘装系统时 ,找不到本地磁盘
- iOS 将16数据转换成2进制字符串
- android文件上传
- SSH学习--struts的action中BaseAction的作用
- 视频框架 Vitamio 使用教程+部分心得 (三) 视频控制器MediaController + 部分中文API
- 基于Theano的深度学习(Deep Learning)框架Keras学习随笔-17-Embedding Layers
- 【FastDev4Android框架开发】Android 数据缓存器ACache的详解和使用(四)
- HDU 3262 Seat taking up is tough
- E哥的Git教程(一)热身篇
- git版本1.7.6的问题
- 摘自<编写高质量代码:改善Java程序的151个建议>