您的位置:首页 > 其它

xUtils下载大文件时,文件未下载完整就返回了SUCCESS状态的解决方案

2015-07-02 10:47 344 查看
相信用过xutil的朋友都遇到过我的这种情况xUtils下载大文件时,文件未下载完整就返回了SUCCESS状态。跟踪了代后发现,xutil中的httpclient在流还没读取完时就返回了-1跳出了while循环。之后在xutil中该下载就被标记为已完成但进度却不是100%,要点击继续下载就可以断点下载(好在不会给文件造成什么其他影响,可是也不爽是不?)。

贴下代码:

bis = new BufferedInputStream(entity.getContent());
bos = new BufferedOutputStream(fileOutputStream);

if (callBackHandler != null && !callBackHandler.updateProgress(total, current, true)) {
return targetFile;
}

byte[] tmp = new byte[4096];
int len;
while ((len = bis.read(tmp)) != -1) {//这里返回了-1
bos.write(tmp, 0, len);
current += len;
if (callBackHandler != null) {
if (!callBackHandler.updateProgress(total, current, false)) {
return targetFile;
}
}
}
bos.flush();
if (callBackHandler != null) {
callBackHandler.updateProgress(total, current, true);
}
} finally {
IOUtils.closeQuietly(bis);
IOUtils.closeQuietly(bos);
}


之后在HttpHandler中修改了代码,解决了这个问题,可是感觉有点治标不治本,哪位朋友有更好的解决方案希望可以分享下!

贴下修改这个BUG的代码,为了方便我把整个类的代码都贴上来了,添加了注释:

public class HttpHandler<T> extends PriorityAsyncTask<Object, Object, Void> implements RequestCallBackHandler {

private final AbstractHttpClient client;
private final HttpContext context;
private HttpRedirectHandler httpRedirectHandler;
private long fileLen = 0;//本地已下载文件的大小
private File downloadFile;//本地已下载文件的对象

public void setHttpRedirectHandler(HttpRedirectHandler httpRedirectHandler) {
if (httpRedirectHandler != null) {
this.httpRedirectHandler = httpRedirectHandler;
}
}

private String requestUrl;
private String requestMethod;
private HttpRequestBase request;
private boolean isUploading = true;
private RequestCallBack<T> callback;

private int retriedCount = 0;
private String fileSavePath = null;
private boolean isDownloadingFile = false;
private boolean autoResume = false; // Whether the downloading could continue from the point of interruption.
private boolean autoRename = false; // Whether rename the file by response header info when the download completely.
private String charset; // The default charset of response header info.

public HttpHandler(AbstractHttpClient client, HttpContext context, String charset, RequestCallBack<T> callback) {
this.client = client;
this.context = context;
this.callback = callback;
this.charset = charset;
this.client.setRedirectHandler(notUseApacheRedirectHandler);
}

private State state = State.WAITING;

public State getState() {
return state;
}

private long expiry = HttpCache.getDefaultExpiryTime();

public void setExpiry(long expiry) {
this.expiry = expiry;
}

public void setRequestCallBack(RequestCallBack<T> callback) {
this.callback = callback;
}

public RequestCallBack<T> getRequestCallBack() {
return this.callback;
}

// 执行请求
@SuppressWarnings("unchecked")
private ResponseInfo<T> sendRequest(HttpRequestBase request) throws HttpException {

HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler();
while (true) {

if ((autoResume && isDownloadingFile)) {
downloadFile = new File(fileSavePath);//这里提升为全局变量方便后面使用

//                long fileLen = 0;
fileLen = 0;//这里提升为全局变量方便后面使用
if (downloadFile.isFile() && downloadFile.exists()) {
fileLen = downloadFile.length();
}
System.out.println("fileLen:"+fileLen);
if (fileLen > 0) {
request.setHeader("RANGE", "bytes=" + fileLen + "-");
}
}

boolean retry = true;
IOException exception = null;
try {
requestMethod = request.getMethod();
if (HttpUtils.sHttpCache.isEnabled(requestMethod)) {
String result = HttpUtils.sHttpCache.get(requestUrl);
if (result != null) {
return new ResponseInfo<T>(null, (T) result, true);
}
}
ResponseInfo<T> responseInfo = null;
if (!isCancelled()) {
HttpResponse response = client.execute(request, context);
responseInfo = handleResponse(response);
}
return responseInfo;
} catch (UnknownHostException e) {
exception = e;
retry = retryHandler.retryRequest(exception, ++retriedCount, context);
} catch (IOException e) {
exception = e;
retry = retryHandler.retryRequest(exception, ++retriedCount, context);
} catch (NullPointerException e) {
exception = new IOException(e.getMessage());
exception.initCause(e);
retry = retryHandler.retryRequest(exception, ++retriedCount, context);
} catch (HttpException e) {
throw e;
} catch (Throwable e) {
exception = new IOException(e.getMessage());
exception.initCause(e);
retry = retryHandler.retryRequest(exception, ++retriedCount, context);
}
if (!retry) {
throw new HttpException(exception);
}
}
}

@Override
protected Void doInBackground(Object... params) {
if (this.state == State.CANCELLED || params == null || params.length == 0) return null;

if (params.length > 3) {
fileSavePath = String.valueOf(params[1]);
isDownloadingFile = fileSavePath != null;
autoResume = (Boolean) params[2];
autoRename = (Boolean) params[3];
}

try {
if (this.state == State.CANCELLED) return null;
// init request & requestUrl
request = (HttpRequestBase) params[0];
requestUrl = request.getURI().toString();
if (callback != null) {
callback.setRequestUrl(requestUrl);
}

this.publishProgress(UPDATE_START);

lastUpdateTime = SystemClock.uptimeMillis();

ResponseInfo<T> responseInfo = sendRequest(request);
if (responseInfo != null) {
//检测下载的文件是否完整,防止HttpClient未下载完成既返回-1使得文件state标记		//为SUCCESS而文件并未下载完整的BUG by Charminglee
responseInfo = checkoutFileLength(responseInfo);

this.publishProgress(UPDATE_SUCCESS, responseInfo);
return null;
}
} catch (HttpException e) {
this.publishProgress(UPDATE_FAILURE, e, e.getMessage());
}

return null;
}
/**
* 递归检测已下载文件是否完整
* @param responseInfo
* @return
* @throws HttpException
*/
private ResponseInfo<T> checkoutFileLength(ResponseInfo<T> responseInfo)
throws HttpException {
<span style="white-space:pre">		</span>if(<span style="font-family: Arial, Helvetica, sans-serif;">downloadFile == null</span><span style="font-family: Arial, Helvetica, sans-serif;">)  </span><span style="font-family: Arial, Helvetica, sans-serif;">downloadFile = new File(fileSavePath);</span><span style="font-family: Arial, Helvetica, sans-serif;">
</span>		System.out.println("本地文件大小:"+downloadFile.length()+"  服务器实际大小:"+(responseInfo.contentLength+fileLen)+"  contentLength:"+responseInfo.contentLength+"  fileLen"+fileLen);
if(downloadFile.length() != (responseInfo.contentLength+fileLen)){
autoResume = true;
System.out.println(fileSavePath+"出现了奇葩情况。。。。。。。。。。");
responseInfo = sendRequest(request);
checkoutFileLength(responseInfo);
}
return responseInfo;
}

private final static int UPDATE_START = 1;
private final static int UPDATE_LOADING = 2;
private final static int UPDATE_FAILURE = 3;
private final static int UPDATE_SUCCESS = 4;

@Override
@SuppressWarnings("unchecked")
protected void onProgressUpdate(Object... values) {
if (this.state == State.CANCELLED || values == null || values.length == 0 || callback == null) return;
switch ((Integer) values[0]) {
case UPDATE_START:
this.state = State.STARTED;
callback.onStart();
break;
case UPDATE_LOADING:
if (values.length != 3) return;
this.state = State.LOADING;
callback.onLoading(
Long.valueOf(String.valueOf(values[1])),
Long.valueOf(String.valueOf(values[2])),
isUploading);
break;
case UPDATE_FAILURE:
if (values.length != 3) return;
this.state = State.FAILURE;
callback.onFailure((HttpException) values[1], (String) values[2]);
break;
case UPDATE_SUCCESS:
if (values.length != 2) return;
this.state = State.SUCCESS;
callback.onSuccess((ResponseInfo<T>) values[1]);
break;
default:
break;
}
}

@SuppressWarnings("unchecked")
private ResponseInfo<T> handleResponse(HttpResponse response) throws HttpException, IOException {
if (response == null) {
throw new HttpException("response is null");
}
if (isCancelled()) return null;

StatusLine status = response.getStatusLine();
int statusCode = status.getStatusCode();
if (statusCode < 300) {
Object result = null;
HttpEntity entity = response.getEntity();
if (entity != null) {
isUploading = false;
if (isDownloadingFile) {
autoResume = autoResume && OtherUtils.isSupportRange(response);
String responseFileName = autoRename ? OtherUtils.getFileNameFromHttpResponse(response) : null;
FileDownloadHandler downloadHandler = new FileDownloadHandler();
result = downloadHandler.handleEntity(entity, this, fileSavePath, autoResume, responseFileName);
} else {
StringDownloadHandler downloadHandler = new StringDownloadHandler();
result = downloadHandler.handleEntity(entity, this, charset);
if (HttpUtils.sHttpCache.isEnabled(requestMethod)) {
HttpUtils.sHttpCache.put(requestUrl, (String) result, expiry);
}
}
}
return new ResponseInfo<T>(response, (T) result, false);
} else if (statusCode == 301 || statusCode == 302) {
if (httpRedirectHandler == null) {
httpRedirectHandler = new DefaultHttpRedirectHandler();
}
HttpRequestBase request = httpRedirectHandler.getDirectRequest(response);
if (request != null) {
return this.sendRequest(request);
}
} else if (statusCode == 416) {
throw new HttpException(statusCode, "maybe the file has downloaded completely");
} else {
throw new HttpException(statusCode, status.getReasonPhrase());
}
return null;
}

/**
* cancel request task.
*/
@Override
public void cancel() {
this.state = State.CANCELLED;

if (request != null && !request.isAborted()) {
try {
request.abort();
} catch (Throwable e) {
}
}
if (!this.isCancelled()) {
try {
this.cancel(true);
} catch (Throwable e) {
}
}

if (callback != null) {
callback.onCancelled();
}
}

private long lastUpdateTime;

@Override
public boolean updateProgress(long total, long current, boolean forceUpdateUI) {
if (callback != null && this.state != State.CANCELLED) {
if (forceUpdateUI) {
this.publishProgress(UPDATE_LOADING, total, current);
} else {
long currTime = SystemClock.uptimeMillis();
if (currTime - lastUpdateTime >= callback.getRate()) {
lastUpdateTime = currTime;
this.publishProgress(UPDATE_LOADING, total, current);
}
}
}
return this.state != State.CANCELLED;
}

public enum State {
WAITING(0), STARTED(1), LOADING(2), FAILURE(3), CANCELLED(4), SUCCESS(5);
private int value = 0;

State(int value) {
this.value = value;
}

public static State valueOf(int value) {
switch (value) {
case 0:
return WAITING;
case 1:
return STARTED;
case 2:
return LOADING;
case 3:
return FAILURE;
case 4:
return CANCELLED;
case 5:
return SUCCESS;
default:
return FAILURE;
}
}

public int value() {
return this.value;
}
}

private static final NotUseApacheRedirectHandler notUseApacheRedirectHandler = new NotUseApacheRedirectHandler();

private static final class NotUseApacheRedirectHandler implements RedirectHandler {
@Override
public boolean isRedirectRequested(HttpResponse httpResponse, HttpContext httpContext) {
return false;
}

@Override
public URI getLocationURI(HttpResponse httpResponse, HttpContext httpContext) throws ProtocolException {
return null;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: