请求头一:
>>>>>>>>>>>>>>>>>>>>>>>>
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]

请求下载整个文件:

  1. GET /test.rar HTTP/1.1
  2. Connection: close
  3. Host: 116.1.219.219
  4. Range: bytes=0-801 //一般请求下载整个文件是bytes=0- 或不用这个头

一般正常回应

  1. HTTP/1.1 200 OK
  2. Content-Length: 801
  3. Content-Type: application/octet-stream
  4. 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. (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 实现起来还是很简单的吧。
    接下来要做的事就是怎么保存获得的流到文件中去了。

  2. 保存文件采用的方法。

    我采用的是 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 实现断点续传的更多相关文章

  1. 用 Java 实现断点续传 (HTTP)

    断点续传的原理 其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有所不同而已. 打个比方,浏览器请求服务器上的一个文时,所发出的请求如下: 假设服务器域名为 wwww.sjtu.edu ...

  2. 用Java实现断点续传的基本思路和代码

    用Java实现断点续传的基本思路和代码   URL url = new URL(http://www.oschina.net/no-exist.zip); HttpURLConnection http ...

  3. 用 Java 实现断点续传参考 (HTTP)

    断点续传的原理 其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有所不同而已.        打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:        假设服务器域名为 ...

  4. Java ftp断点续传

    FtpTransFile类 import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundExcept ...

  5. Java实现断点续传

    原理: 断点续传的关键是断点,所以在制定传输协议的时候要设计好,如上图,我自定义了一个交互协议,每次下载请求都会带上下载的起始点,这样就可以支持从断点下载了,其实HTTP里的断点续传也是这个原理,在H ...

  6. java服务器端断点续传

    Servlet Java代码 复制代码 收藏代码 import java.io.BufferedOutputStream; import java.io.File; import java.io.IO ...

  7. java 下载 断点续传

    1 import java.io.BufferedInputStream; 2 import java.io.File; 3 import java.io.FileInputStream; 4 imp ...

  8. java文件断点续传上传下载解决方案

    这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数 下面直接贴代码吧,一些难懂的我大部分都加上注释了: 上传文件实体类: 看得 ...

  9. java支持断点续传文件上传和下载组件

    java两台服务器之间,大文件上传(续传),采用了Socket通信机制以及JavaIO流两个技术点,具体思路如下: 实现思路: 1.服:利用ServerSocket搭建服务器,开启相应端口,进行长连接 ...

随机推荐

  1. ABP 样板开发框架系列

    --ABP 官网与源码 http://www.aspnetboilerplate.com/ https://github.com/aspnetboilerplate --pdf和docx 文档 htt ...

  2. Oracle等待事件之db file sequential read/ db file parallel read

    1.产生原因 db file sequential read这个是非常常见的I/O 相关的等待事件.表示发生了与索引扫描相关的等待.意味着I/O 出现了问题,通常表示I/O竞争或者I/O 需求太多. ...

  3. kubernetes实战(八):k8s集群安全机制RBAC

    1.基本概念 RBAC(Role-Based Access Control,基于角色的访问控制)在k8s v1.5中引入,在v1.6版本时升级为Beta版本,并成为kubeadm安装方式下的默认选项, ...

  4. 每日算法之三十五:Wildcard Matching

    模式匹配的实现,'?'代表单一字符,'*'代表随意多的字符.写代码实现两个字符串是否匹配. Implement wildcard pattern matching with support for ' ...

  5. centos单用户 救援 运行级别 yum,单用户模式,救援模式,inittab :启动级别 e2fsck wetty mingetty 物理终端 /dev/console 虚拟终端 /dev/tty(0,6) 模拟终端 /dev/pts/# grub-md5-crypt 给grub加密码 initrd 第二节课

    centos单用户 救援 运行级别  yum,单用户模式,救援模式,inittab :启动级别  e2fsck  wetty  mingetty  物理终端 /dev/console  虚拟终端 /d ...

  6. PhotoSwipe中文API(五)

    Responsive Images PhotoSwipe不支持<图片>或srcset,因为它要求所定义的图像的尺寸,并使用延迟加载.但是,随着图像动态加载,它很容易切换人士透露,即便是在旧 ...

  7. mydumper原理介绍

      mydumper的安装:http://www.cnblogs.com/lizhi221/p/7010174.html   mydumper介绍   MySQL自身的mysqldump工具支持单线程 ...

  8. 史上最全的MonkeyRunner自动化测试从入门到精通(3)

    原文地址https://blog.csdn.net/liu_jing_hui/article/details/60956088 MonkeyRunner复杂的功能开始学习 (1)获取APK文件中ID的 ...

  9. java基础知识面试题(41-95)

    41.日期和时间:- 如何取得年月日.小时分钟秒?- 如何取得从1970年1月1日0时0分0秒到现在的毫秒数?- 如何取得某月的最后一天?- 如何格式化日期?答:问题1:创建java.util.Cal ...

  10. forEach方法的实现

    var arr = [1, 23, 1, 1, 1, 3, 23, 5, 6, 7, 9, 9, 8, 5]; Array.prototype.forEach = Array.prototype.fo ...