码迷,mamicode.com
首页 > 编程语言 > 详细

java 实现断点续传

时间:2015-12-02 20:33:07      阅读:188      评论:0      收藏:0      [点我收藏+]

标签:

 

/**
     * 下载服务器已存在的文件,支持断点续传
     * 
     * @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");
            String isoFileName = URLEncoder.encode(proposeFile.getName(), ENCODING);
            response.addHeader("Content-Disposition", "attachment; filename=" + isoFileName);
            response.addHeader("Content-Length", String.valueOf(fSize));
            response.setHeader("Accept-Ranges", "bytes");
            long pos = 0, last = fSize - 1;
            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;
                }
            }
            String contentRange = new StringBuffer("bytes ").append(new Long(pos).toString()).append("-").append(new Long(fSize - 1).toString()).append("/").append(new Long(fSize).toString()).toString();
            response.setHeader("Content-Range", contentRange);

            // 跳过已经下载的部分,进行后续下载
            bufferOut = new BufferedOutputStream(response.getOutputStream());
            inputStream = new BufferedInputStream(new FileInputStream(proposeFile));
            inputStream.skip(pos);
            byte[] buffer = new byte[5 * 1024];
            long rangLength = last - pos + 1, sum = 0;// sum记录总共已经读取了多少字节
            int length = 0;
            while ((length = inputStream.read(buffer, 0, ((rangLength - sum) <= buffer.length ? ((int) (rangLength - sum)) : buffer.length))) != -1 && sum < rangLength) {
                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();
            }
        }
    

 

断点续传的原理

其实断点续传的原理很简单,就是在 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=106786028        
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-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 实现断点续传

标签:

原文地址:http://www.cnblogs.com/liaojie970/p/5013790.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!