断点续传就是在下载资源过程中,网络出现中断后,下一次下载的时候能够接着上一次下载的地方继续下载,以达到节省流量及时间的效果。在Http1.1协议中默认支持获取文件的部分内容,这其中主要是通过头部的两个参数:Range和Content Range来实现的。客户端发请求时对应的是Range,服务器端响应时对应的是 Content-Range。
栗子如下:
@Entity(tableName = "BreakPointFileTable")
public class BreakPointFileTable {
@PrimaryKey(autoGenerate = true)
public long id;
public String fileName;
public String fileDownloadUrl;
public String fileSavePath;
public long fileLength;
public long startDownloadProgress;
public long currentDownloadProgress;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileDownloadUrl() {
return fileDownloadUrl;
}
public void setFileDownloadUrl(String fileDownloadUrl) {
this.fileDownloadUrl = fileDownloadUrl;
}
public String getFileSavePath() {
return fileSavePath;
}
public void setFileSavePath(String fileSavePath) {
this.fileSavePath = fileSavePath;
}
public long getFileLength() {
return fileLength;
}
public void setFileLength(long fileLength) {
this.fileLength = fileLength;
}
public long getStartDownloadProgress() {
return startDownloadProgress;
}
public void setStartDownloadProgress(long startDownloadProgress) {
this.startDownloadProgress = startDownloadProgress;
}
public long getCurrentDownloadProgress() {
return currentDownloadProgress;
}
public void setCurrentDownloadProgress(long currentDownloadProgress) {
this.currentDownloadProgress = currentDownloadProgress;
}
}
public void download(final String url, final String fileName, final OnDownloadListener listener) {
RandomAccessFile finalRandomFile = null;
long downLoadFileLength = 0;
long start = 0;
File dir = createDir(FileTypeUtils.getFileType(fileName),getFileName(fileName));
String name = fileName;
File file = new File(dir, name);
try {
boolean fileDownloadExits = BreakPointDataBase.getDataBase().getResumeBreakPointDao().isFileDownloadExits(fileName);
if (fileDownloadExits) {
try {
finalRandomFile = new RandomAccessFile(file, "rwd");
List<BreakPointFileTable> allDownloadFileList = BreakPointDataBase.getDataBase().getResumeBreakPointDao().getDownloadFileByName(fileName);
if(allDownloadFileList.size()>0) {
start = allDownloadFileList.get(0).getStartDownloadProgress() + allDownloadFileList.get(0).getCurrentDownloadProgress();
downLoadFileLength = allDownloadFileList.get(0).getFileLength();
finalRandomFile.seek(start);
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
file.createNewFile();
finalRandomFile = new RandomAccessFile(file, "rwd");
finalRandomFile.seek(0);
BreakPointFileTable breakPointFileTable = new BreakPointFileTable();
breakPointFileTable.setFileName(file.getName());
breakPointFileTable.setFileDownloadUrl(url);
breakPointFileTable.setStartDownloadProgress(0);
breakPointFileTable.setCurrentDownloadProgress(0);
breakPointFileTable.setFileSavePath(file.getPath());
BreakPointDataBase.getDataBase().getResumeBreakPointDao().insertDownloadFileInfo(breakPointFileTable);
}
Request.Builder builder = new okhttp3.Request.Builder();
builder.addHeader("Authorization", "Basic " + com.decard.utils.Base64Util.encode(String.format("%s:%s", userName, password).getBytes()));
if(start > 0 && downLoadFileLength > 0) {
// 断点续传关键
builder.addHeader("RANGE", "bytes=" + start + "-" + downLoadFileLength);
}
RandomAccessFile fileOperator = finalRandomFile;
okhttp3.Request request = builder.url(url).build();
call = okHttpClient.newCall(request);
long finalStart = start;
long finalDownLoadFileLength = downLoadFileLength;
call.enqueue(new okhttp3.Callback() {
long tempCurrentProgress = 0;
long tempFileLength = 0;
@Override
public void onFailure(okhttp3.Call call, java.io.IOException e) {
listener.onDownloadFailed();
}
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws java.io.IOException {
java.io.InputStream is = null;
byte[] buf = new byte[1024*5];
int len = 0;
java.io.FileOutputStream fos = null;
try {
// 下载中处理
// 下载完成
BreakPointDataBase.getDataBase().getResumeBreakPointDao().deleteBreakPointFileInfo(
BreakPointDataBase.getDataBase().getResumeBreakPointDao().getAllDownloadFileList());
listener.onDownloadSuccess(file);
} catch (Exception e) {
// 网络异常 更新数据库
BreakPointDataBase.getDataBase().getResumeBreakPointDao()
.updateDownloadFileProgress(url, tempCurrentProgress, tempFileLength);
e.printStackTrace();
listener.onDownloadFailed();
} finally {
...
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
注意: 在断点续传中 第一次能够获取文件总长度,续传过程中 获取的是文件部分长度,所以在计算进度时需要注意。