什么叫断点下载?迅雷就可以断点下载!就是说下载了一半,然后把下载中断,明天打开电脑再继续下载。如果我在下载一个文件,先下载了50%,然后停止下载,然后再继续下载省下的50%。这里有两个问题需要我们处理:
- 需要告诉服务器,我要从资源的某个字节位置开始下载,而不是从头开始下载;
- 下载到的数据要追加到原来已下载文件的尾部,而不是创建新文件。
第一个问题我们应该知道怎么处理,我们在学习IO流时应该知道FileOutputStream类的构造器可以接受一人boolean类型的参数:
newFileOutputStream(“a.txt”, true);
这表示如果a.txt文件不存在,那么创建它;如果a.txt文件存在,那么就把写入到流中的数据追加到a.txt文件的尾部。
第二个问题是需要通过请求头信息来处理,有一个请求头叫range:
Range头域可以请求实体的一个或者多个子范围。
例如, 表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
但是服务器可以忽略此请求头,如果无条件GET包含Range请求头,响应会以状态码206(PartialContent)返回而不是以200 (OK)。有了这个请求头,我们就可以通过请求头告诉服务器,从资源的哪个位置开始下载,而不是从头开始下载了。但是,我们上一次下载了多少呢?我们需要告诉服务器从上一次下载的位置开始下载,但我们怎么知道上一次下载了多少呢?其实很简单就知道了,你下载的数据都写到目标文件中了,也就是那个还没有下载完的文件,一半的文件,你看看它的长度不就知道了么!
File file = new File(path);
long length = file.length();
2,具体代码实现:
/**
* 断点下载
* @throws InterruptedException
*/
@Test public void testDebugDownload() throws IOException, InterruptedException{
//创建URL对象
URL url = new URL("http://localhost:8080/****/resource/kiss.avi");
//使用url获取HttpURLConnection对象
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//客户端的请求方式
conn.setRequestMethod("GET");
//已经下载的字节数
long alreadySize = 0;
//将文件写到d:\kiss.avi中
File file = new File("d:\\kiss.avi");
//如果存在,说明原来下载过,不过可能没有下载完
if(file.exists()){
//如果文件存在,就获取当前文件的大小
alreadySize = file.length();
}
/**
* Range头域可以请求实体的一个或者多个子范围。
* 例如: 表示头500个字节:bytes=0-499
* 表示第二个500字节:bytes=500-999
* 表示最后500个字节:bytes=-500
* 表示500字节以后的范围:bytes=500-
* 第一个和最后一个字节:bytes=0-0,-1
* 同时指定几个范围:bytes=500-600,601-999
* 但是服务器可以忽略此请求头,如果无条件GET包含Range请求头,响应会以状态码206(PartialContent)返回而不是以200 (OK)。
*/
conn.addRequestProperty("range", "bytes=" + alreadySize + "-");
conn.connect();
//206,一般表示断点续传
//获取服务器回馈的状态码
int code = conn.getResponseCode();
// 如果响应成功,因为使用了range请求头,那么响应成功的状态码为206,而不是200
if(code == 206){
//获取未下载的文件的大小
// 本方法用来获取响应正文的大小,但因为设置了range请求头,那么这个方法返回的就是剩余的大小
long unfinishedSize = conn.getContentLength();
//文件的大小
long size = alreadySize + unfinishedSize;
//获取输入流
InputStream in = conn.getInputStream();
//获取输出对象,参数一:目标文件,参数2表示在原来的文件中追加
OutputStream out = new BufferedOutputStream(new FileOutputStream(file,true));
//开始下载
byte[] buff = new byte[2048];
int len;
StringBuilder sb = new StringBuilder();
while((len = in.read(buff)) != -1){
out.write(buff, 0, len);
//将下载的累加到alreadSize中
alreadySize += len;
//下载进度
System.out.printf("%.2f%%\n", alreadySize * 1.0 / size * 100);
//由于文件大小可以看得到,那么我们这里使用阻塞
Thread.sleep(2);
}
out.close();
System.out.println("下载完成!!!");
}else{
System.out.println("下载失败!!!");
}
//断开连接
conn.disconnect();
}