java 实现断点续传
请求头一:
>>>>>>>>>>>>>>>>>>>>>>>>
range:bytes=1024- //断点续传请求必须包含该请求头
host:192.168.118.120:8888
accept:*/*
>>>>>>>>>>>>>>>>>>>>>>>>
响应头一:
>>>>>>>>>>>>>>>>>>>>>>>>
Server: Apache-Coyote/1.1
Content-Disposition: attachment; filename=WebGoat-OWASP_Developer-5.2.zip
Accept-Ranges: bytes
Content-Range: bytes 1024-304974591/304974592
Content-Type: application/x-download;charset=utf-8
Content-Length: 304973568 //需要特别注意这里长度值为请求需要的长度,即304974591 -
>>>>>>>>>>>>>>>>>>>>>>>>
请求头二:
>>>>>>>>>>>>>>>>>>>>>>>>
range:bytes=10-1033 //断点续传请求必须包含该请求头
host:192.168.118.120:8888
accept:*/*
>>>>>>>>>>>>>>>>>>>>>>>>
响应头二:
>>>>>>>>>>>>>>>>>>>>>>>>
Server: Apache-Coyote/1.1
Content-Disposition: attachment; filename=WebGoat-OWASP_Developer-5.2.zip
Accept-Ranges: bytes
Content-Range: bytes 10-1033/304974592
Content-Type: application/x-download;charset=utf-8
Content-Length: 1024 //需要特别注意这里长度值为请求需要的长度,即1033- 10
>>>>>>>>>>>>>>>>>>>>>>>>
/**
* 下载服务器已存在的文件,支持断点续传
*
* @param request
* 请求对象
* @param response
* 响应对象
* @param path
* 文件路径(绝对)
*/
public static void download(HttpServletRequest request, HttpServletResponse response, File proposeFile) {
LOGGER.debug("下载文件路径:" + proposeFile.getPath());
InputStream inputStream = null;
OutputStream bufferOut = null;
try {
// 设置响应报头
long fSize = proposeFile.length();
response.setContentType("application/x-download");
// Content-Disposition: attachment; filename=WebGoat-OWASP_Developer-5.2.zip
response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(proposeFile.getName(), ENCODING));
// Accept-Ranges: bytes
response.setHeader("Accept-Ranges", "bytes");
long pos = 0, last = fSize - 1, sum = 0;//pos开始读取位置; last最后读取位置; sum记录总共已经读取了多少字节
if (null != request.getHeader("Range")) {
// 断点续传
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
try {
// 情景一:RANGE: bytes=2000070- 情景二:RANGE: bytes=2000070-2000970
String numRang = request.getHeader("Range").replaceAll("bytes=", "");
String[] strRange = numRang.split("-");
if (strRange.length == 2) {
pos = Long.parseLong(strRange[0].trim());
last = Long.parseLong(strRange[1].trim());
} else {
pos = Long.parseLong(numRang.replaceAll("-", "").trim());
}
} catch (NumberFormatException e) {
LOGGER.error(request.getHeader("Range") + " is not Number!");
pos = 0;
}
}
long rangLength = last - pos + 1;// 总共需要读取的字节
// Content-Range: bytes 10-1033/304974592
String contentRange = new StringBuffer("bytes ").append(pos).append("-").append(last).append("/").append(fSize).toString();
response.setHeader("Content-Range", contentRange);
// Content-Length: 1024
response.addHeader("Content-Length", String.valueOf(rangLength)); // 跳过已经下载的部分,进行后续下载
bufferOut = new BufferedOutputStream(response.getOutputStream());
inputStream = new BufferedInputStream(new FileInputStream(proposeFile));
inputStream.skip(pos);
byte[] buffer = new byte[1024];
int length = 0;
while (sum < rangLength) {
length = inputStream.read(buffer, 0, ((rangLength - sum) <= buffer.length ? ((int) (rangLength - sum)) : buffer.length));
sum = sum + length;
bufferOut.write(buffer, 0, length);
}
} catch (Throwable e) {
if (e instanceof ClientAbortException) {
// 浏览器点击取消
LOGGER.info("用户取消下载!");
} else {
LOGGER.info("下载文件失败....");
e.printStackTrace();
}
} finally {
try {
if (bufferOut != null) {
bufferOut.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
开发中遇到的一个错误提示:
org.apache.catalina.connector.ClientAbortException: Connection reset by peer: socket write error
该错误的原因就是因为上面的Content-Length: 1024 与请求头重请求的长度不一致,导致了请求端拒绝了
http断点续传原理:http头 Range、Content-Range
所谓断点续传,也就是要从文件已经下载的地方开始继续下载。在以前版本的 HTTP 协议是不支持断点的,HTTP/1.1 开始就支持了。一般断点下载时才用到 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]
请求下载整个文件:
- GET /test.rar HTTP/1.1
- Connection: close
- Host: 116.1.219.219
- Range: bytes=0-801 //一般请求下载整个文件是bytes=0- 或不用这个头
一般正常回应
- HTTP/1.1 200 OK
- Content-Length: 801
- Content-Type: application/octet-stream
- Content-Range: bytes 0-800/801 //801:文件总大小
以下是摘取网络中的一段内容,并进了修改:原始的内容有误导致被坑
断点续传的原理
其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有所不同而已。
打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:
假设服务器域名为 wwww.sjtu.edu.cn,文件名为 down.zip。
GET /down.zip HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-
excel, application/msword, application/vnd.ms-powerpoint, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
Connection: Keep-Alive
服务器收到请求后,按要求寻找请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:
200
Content-Length=106786028
Accept-Ranges=bytes
Date=Mon, 30 Apr 2001 12:56:11 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT
所谓断点续传,也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给 Web 服务器的时候要多加一条信息 -- 从哪里开始。
下面是用自己编的一个"浏览器"来传递请求信息给 Web 服务器,要求从 2000070 字节开始。
GET /down.zip HTTP/1.0
User-Agent: NetFox
RANGE: bytes=2000070-
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
仔细看一下就会发现多了一行 RANGE: bytes=2000070-
这一行的意思就是告诉服务器 down.zip 这个文件从 2000070 字节开始传,前面的字节不用传了。
服务器收到这个请求以后,返回的信息如下:
206
Content-Length=106585958
Content-Range=bytes 2000070-106786027/106786028
Date=Mon, 30 Apr 2001 12:55:20 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT
和前面服务器返回的信息比较一下,就会发现变化:
Content-Length=106585958
Content-Range=bytes 2000070-106786027/106786028
返回的代码也改为 206 了,而不再是 200 了。
知道了以上原理,就可以进行断点续传的编程了
Java 实现断点续传的关键几点
- (1) 用什么方法实现提交 RANGE: bytes=2000070-。
当然用最原始的 Socket 是肯定能完成的,不过那样太费事了,其实 Java 的 net 包中提供了这种功能。代码如下:URL url = new URL("http://www.sjtu.edu.cn/down.zip");
HttpURLConnection httpConnection =
(HttpURLConnection)url.openConnection();// 设置 User-Agent
httpConnection.setRequestProperty("User-Agent","NetFox");
// 设置断点续传的开始位置
httpConnection.setRequestProperty("RANGE","bytes=2000070");
// 获得输入流
InputStream input = httpConnection.getInputStream();从输入流中取出的字节流就是 down.zip 文件从 2000070 开始的字节流。
大家看,其实断点续传用 Java 实现起来还是很简单的吧。
接下来要做的事就是怎么保存获得的流到文件中去了。 - 保存文件采用的方法。
我采用的是 IO 包中的 RandAccessFile 类。
操作相当简单,假设从 2000070 处开始保存文件,代码如下:
RandomAccess oSavedFile = new
RandomAccessFile("down.zip","rw");
long nPos = 2000070;
// 定位文件指针到 nPos 位置
oSavedFile.seek(nPos);
byte[] b = new byte[1024];
int nRead;
// 从输入流中读入字节流,然后写到文件中
while((nRead=input.read(b,0,1024)) > 0)
{
oSavedFile.write(b,0,nRead);
}
怎么样,也很简单吧。
接下来要做的就是整合成一个完整的程序了。包括一系列的线程控制等等。
注:转载http://www.ibm.com/developerworks/cn/java/joy-down/index.html
java 实现断点续传的更多相关文章
- 用 Java 实现断点续传 (HTTP)
断点续传的原理 其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有所不同而已. 打个比方,浏览器请求服务器上的一个文时,所发出的请求如下: 假设服务器域名为 wwww.sjtu.edu ...
- 用Java实现断点续传的基本思路和代码
用Java实现断点续传的基本思路和代码 URL url = new URL(http://www.oschina.net/no-exist.zip); HttpURLConnection http ...
- 用 Java 实现断点续传参考 (HTTP)
断点续传的原理 其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有所不同而已. 打个比方,浏览器请求服务器上的一个文时,所发出的请求如下: 假设服务器域名为 ...
- Java ftp断点续传
FtpTransFile类 import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundExcept ...
- Java实现断点续传
原理: 断点续传的关键是断点,所以在制定传输协议的时候要设计好,如上图,我自定义了一个交互协议,每次下载请求都会带上下载的起始点,这样就可以支持从断点下载了,其实HTTP里的断点续传也是这个原理,在H ...
- java服务器端断点续传
Servlet Java代码 复制代码 收藏代码 import java.io.BufferedOutputStream; import java.io.File; import java.io.IO ...
- java 下载 断点续传
1 import java.io.BufferedInputStream; 2 import java.io.File; 3 import java.io.FileInputStream; 4 imp ...
- java文件断点续传上传下载解决方案
这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数 下面直接贴代码吧,一些难懂的我大部分都加上注释了: 上传文件实体类: 看得 ...
- java支持断点续传文件上传和下载组件
java两台服务器之间,大文件上传(续传),采用了Socket通信机制以及JavaIO流两个技术点,具体思路如下: 实现思路: 1.服:利用ServerSocket搭建服务器,开启相应端口,进行长连接 ...
随机推荐
- 洛谷P3939 数颜色 二分查找
正解:二分 解题报告: 传送门! 话说其实我开始看到这题想到的是分块,,, 但是显然不用这么复杂,,,因为仔细看下这题,会发现每次只改变相邻的兔子的位置 所以开个vector(或者开个数组也成QwQ( ...
- Ubuntu18.04下安装比特币客户端
一.下载有两种安装方式:安装包和源码 二.安装1.通过安装包安装在https://bitcoin.org/en/download下载Windows,Mac OSX,Linux对应的安装包.安装过程比较 ...
- 测试命令运行时间:timeit和profile
先说timeit from timeit import Timer def test1(): n=0 for i in range(101): n+=i return n def test2(): r ...
- innobackupex 还原和备份实例
InnoDB 和非 InnoDB 文件的备份都是通过拷贝文件来做的,但是实现的方式不同,前者是以page为粒度做的(xtrabackup),后者是 cp 或者 tar 命令(innobackupex) ...
- [RGEOS]数学基础
1.向量Vector3d using System; namespace RGeos.Geometry { /// <summary> /// 3D向量类 /// </summary ...
- 模块讲解----subprocess模块
历史 #输出结果到屏幕上,并不返回执行状态os.system('dir')#保存命令的执行结果输出ret = os.popen('dir').read() 问题:上面2条是把命令结果保存下来了,但是返 ...
- Ubuntu下virtualenv 的安装及使用
按照这个命令做下来基本是ok的. https://blog.csdn.net/qq_33371343/article/details/78047853
- Django组件补充(缓存,信号,序列化)
Django组件补充(缓存,信号,序列化) Django的缓存机制 1.1 缓存介绍 1.缓存的简介 在动态网站中,用户所有的请求,服务器都会去数据库中进行相应的增,删,查,改,渲染模板,执行业务逻辑 ...
- cocos代码研究(15)Widget子类CheckBox学习笔记
理论基础 复选框是一种特定类型的“两状态”按钮,可以处于“选中”和“未选中状态”.继承自AbstractCheckButton.注 AbstractCheckButton继承自Widget类. 代码部 ...
- 【cocos2d-x 3.5】Lua动画API
1.加载动画 local node = cc.CSLoader:createNode("ActionTimeline/DemoPlayer_skeleton.csb") local ...