接昨天,断点续传下载内容.
因为要后台执行下载,所以这部分直接使用线程实现
直接贴代码,需要注意的内容主要有以下几点
最基本的,先写文件后更新游标
其次,每次任务启动都需要对比存储的最大文件长度和新请求的文件的长度,以防万一
最后,注意清理临时文件.
另外,这份代码只适用于服务器端每次请求返回的文件相同,且无认证的情况
public class ThreadDownLoad extends Thread {
private String taskId;
private String filename;
private String url;
private String tmpfilename;
private long startPos;
private long endPos;
private RandomAccessFile downloadfile = null;
private RandomAccessFile rantmpfile = null;
public ThreadDownLoad(String fileName, String downLoadUrl) {
this.filename = fileName;
this.url = downLoadUrl;
this.taskId = IdUtil.simpleUUID();
}
@Override
public void run() {
log.info("Download thread " + taskId + " has started!!");
if (StrUtil.isEmpty(filename)) {
filename = url.substring(url.lastIndexOf('/') + 1, url
.contains("?") ? url.lastIndexOf('?') : url.length());
}
tmpfilename = filename + "_tmp";
File file = null;
File tmpfile = null;
HttpURLConnection httpcon = null;
InputStream is = null;
int length = 0;
try {
URL downLoadUrl = new URL(url);
httpcon = (HttpURLConnection) downLoadUrl.openConnection();
long fileLength = httpcon.getContentLengthLong();//获取请求资源的总长度。
file = new File(filename);
tmpfile = new File(tmpfilename);
if (file.exists() && file.length() == fileLength) {
System.out
.println("the file you want to download has exited!!");
return;
} else {
//读取上次下载位置,从新开始的话,设置当前位置
RandomAccessFile rantmpfile = null;
if (tmpfile.exists()) {
log.info("the download has continued!!");
rantmpfile = new RandomAccessFile(tmpfile, "rw");
rantmpfile.seek(8);
startPos = rantmpfile.readLong();
rantmpfile.seek(8 * 1000 + 16);
endPos = rantmpfile.readLong();
if (endPos != fileLength) {
log.error("两次下载的文件总大小不一致,需要重新开始下载");
setBegining(tmpfile, fileLength);
}
} else {
log.info("the tmpfile is not available!!");
setBegining(tmpfile, fileLength);
}
if (rantmpfile != null) {
rantmpfile.close();
}
}
httpcon.disconnect();
/*
预处理工作完成,正式开始下载数据
*/
downloadfile = new RandomAccessFile(this.filename, "rw");
rantmpfile = new RandomAccessFile(this.tmpfilename, "rw");
//重新打开连接下载数据
httpcon = (HttpURLConnection) downLoadUrl.openConnection();
//防止网络阻塞,设置指定的超时时间;单位都是ms。超过指定时间,就会抛出异常
//读取数据的超时设置
httpcon.setReadTimeout(20000);
//连接的超时设置
httpcon.setConnectTimeout(20000);
if (startPos < endPos) {
//向服务器请求指定区间段的数据,这是实现断点续传的根本。
httpcon.setRequestProperty("Range", "bytes=" + startPos
+ "-" + endPos);
log.info(taskId + " the remaining size:---- " + (endPos - startPos));
downloadfile.seek(startPos);
if (httpcon.getResponseCode() != HttpURLConnection.HTTP_OK
&& httpcon.getResponseCode() != HttpURLConnection.HTTP_PARTIAL) {
httpcon.disconnect();
downloadfile.close();
log.error("网络异常,请检查资源是否可用");
}
is = httpcon.getInputStream();//获取服务器返回的资源流
byte[] buf = new byte[1024];
while ((length = is.read(buf)) != -1) {
downloadfile.write(buf, 0, length);
/*
每写入一部分数据,将起始位置后移指定长度,并记录在文件总,为下次做准备,确保因意外结束时,可以继续下载,而不是从头开始
可能会出现某一小块数据重新下载的情况,不影响实际效果
*/
startPos += length;
rantmpfile.seek(8);
rantmpfile.writeLong(startPos);
}
//关闭流
is.close();
httpcon.disconnect();
downloadfile.close();
rantmpfile.close();
}
log.info("the thread " + taskId + " has done!!");
// break;
tmpfile.deleteOnExit();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private RandomAccessFile setBegining(File tmpfile, long fileLength) throws IOException {
RandomAccessFile rantmpfile;
rantmpfile = new RandomAccessFile(tmpfile, "rw");
startPos = 0;
endPos = fileLength;
rantmpfile.seek(8);
rantmpfile.writeLong(startPos);
rantmpfile.seek(8 * 1000 + 16);
rantmpfile.writeLong(endPos);
rantmpfile.close();
return rantmpfile;
}
}