volley同样不适合大文件的下载,只能自己扩展了,我这里参考了Netroid,有兴趣的可以去分析下,文件下载模仿了图片的加载方式,添加了两个类分别是FileDownloadRequest和FileDownloader。断点续传是根据http头里的Range 和Content-Range实现.
Range用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式:
Range:(unit=first byte pos)-[last byte pos]
Content-Range用于响应头,指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。一般格式:
Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]
例如:
Range: bytes=0-801 一般下载整个文件是 0-,没有后边的801
Content-Range: bytes 2753525-41526127/41526128 斜杠后边的是文件总大小
关键代码:
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String,String> map = new HashMap<String,String>();
map.put("Range", "bytes=" + mTemporaryFile.length() + "-");
return map;
}
public byte[] handleResponse(HttpResponse response, ResponseDelivery delivery) throws IOException, ServerError {
// Content-Length might be negative when use HttpURLConnection because it default header Accept-Encoding is gzip,
// we can force set the Accept-Encoding as identity in prepare() method to slove this problem but also disable gzip response.
HttpEntity entity = response.getEntity();
long fileSize = entity.getContentLength();
if (fileSize <= 0) {
VolleyLog.d("Response doesn't present Content-Length!");
}
long downloadedSize = mTemporaryFile.length();
boolean isSupportRange = HttpUtils.isSupportRange(response);
if (isSupportRange) {
fileSize += downloadedSize;
// Verify the Content-Range Header, to ensure temporary file is part of the whole file.
// Sometime, temporary file length add response content-length might greater than actual file length,
// in this situation, we consider the temporary file is invalid, then throw an exception.
String realRangeValue = HttpUtils.getHeader(response, "Content-Range");
Log.e("FileDownloadRequest","response header Content-Range:" + realRangeValue);
// response Content-Range may be null when "Range=bytes=0-"
if (!TextUtils.isEmpty(realRangeValue)) {
String assumeRangeValue = "bytes " + downloadedSize + "-" + (fileSize - 1);
Log.e("FileDownloadRequest","assumeRangeValue:" + assumeRangeValue);
if (TextUtils.indexOf(realRangeValue, assumeRangeValue) == -1) {
throw new IllegalStateException(
"The Content-Range Header is invalid Assume[" + assumeRangeValue + "] vs Real[" + realRangeValue + "], " +
"please remove the temporary file [" + mTemporaryFile + "].");
}
}
}
// Compare the store file size(after download successes have) to server-side Content-Length.
// temporary file will rename to store file after download success, so we compare the
// Content-Length to ensure this request already download or not.
if (fileSize > 0 && mStoreFile.length() == fileSize) {
// Rename the store file to temporary file, mock the download success. ^_^
mStoreFile.renameTo(mTemporaryFile);
// Deliver download progress.
delivery.postProgress(this, fileSize, fileSize);
return null;
}
RandomAccessFile tmpFileRaf = new RandomAccessFile(mTemporaryFile, "rw");
// If server-side support range download, we seek to last point of the temporary file.
if (isSupportRange) {
tmpFileRaf.seek(downloadedSize);
} else {
// If not, truncate the temporary file then start download from beginning.
tmpFileRaf.setLength(0);
downloadedSize = 0;
}
try {
InputStream in = entity.getContent();
// Determine the response gzip encoding, support for HttpClientStack download.
if (HttpUtils.isGzipContent(response) && !(in instanceof GZIPInputStream)) {
in = new GZIPInputStream(in);
}
byte[] buffer = new byte[6 * 1024]; // 6K buffer
int offset;
while ((offset = in.read(buffer)) != -1) {
tmpFileRaf.write(buffer, 0, offset);
downloadedSize += offset;
delivery.postProgress(this, fileSize, downloadedSize);
if (isCanceled()) {
delivery.postCancel(this);
break;
}
}
} finally {
try {
// Close the InputStream and release the resources by "consuming the content".
if (entity != null) entity.consumeContent();
} catch (Exception e) {
// This can happen if there was an exception above that left the entity in
// an invalid state.
VolleyLog.v("Error occured when calling consumingContent");
}
tmpFileRaf.close();
}
return null;
}
demo连接: http://download.youkuaiyun.com/detail/s569646547/9098291