java断点续传

转载别人的,用作参考资料,谢谢原作者 https://www.cnblogs.com/liaojie970/p/5013790.html

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 - 1024
>>>>>>>>>>>>>>>>>>>>>>>>

请求头二:
>>>>>>>>>>>>>>>>>>>>>>>>
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);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java 断点续传的代码实现可以分为以下几步: 1. 记录文件已经下载的长度 2. 设置 HTTP 请求头中 "Range" 字段,告诉服务器从文件的哪个位置开始下载 3. 使用 Java I/O 流下载文件 4. 保存文件已经下载的长度,以便下次继续下载 以下是一个简单的 Java 代码示例: ``` import java.io.*; import java.net.HttpURLConnection; import java.net.URL; public class Downloader { private static final int BUFFER_SIZE = 4096; public static void downloadFile(String fileURL, String saveDir) throws IOException { URL url = new URL(fileURL); HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); int responseCode = httpConn.getResponseCode(); // 检查 HTTP 响应码 if (responseCode == HttpURLConnection.HTTP_OK) { String fileName = ""; String disposition = httpConn.getHeaderField("Content-Disposition"); String contentType = httpConn.getContentType(); int contentLength = httpConn.getContentLength(); if (disposition != null) { // 检索文件名 int index = disposition.indexOf("filename="); if (index > 0) { fileName = disposition.substring(index + 10, disposition.length() - 1); } } else { // 检索文件名 fileName = fileURL.substring(fileURL.lastIndexOf("/") + 1, fileURL.length()); } System.out.println("Content-Type = " + contentType); System.out.println("Content-Disposition = " + disposition); System.out.println("Content-Length = " + contentLength); System.out.println("fileName = " + fileName); // 打开本地文件输出流 OutputStream outputStream = new FileOutputStream(saveDir + File.separator + fileName); // 打开网络输入流 InputStream inputStream = httpConn.getInputStream(); int bytesRead = -1; byte[] buffer = new byte[BUFFER_SIZE]; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0 ### 回答2: 断点续传是指在文件传输过程中,当传输中断时,重新开始传输时能够从中断点继续传输,而不是重新开始。下面给出一个使用 Java 实现断点续传的简单示例代码。 ```java import java.io.*; import java.net.HttpURLConnection; import java.net.URL; public class ResumeDownload { public static void main(String[] args) { String fileUrl = "http://example.com/file.zip"; // 需要下载的文件地址 String savePath = "C:/Downloads/file.zip"; // 文件保存路径 try { URL url = new URL(fileUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); File file = new File(savePath); long downloaded = 0; // 已下载的文件大小 if (file.exists()) { downloaded = file.length(); connection.setRequestProperty("Range", "bytes=" + downloaded + "-"); } connection.connect(); FileOutputStream outputStream = new FileOutputStream(file, true); // 追加写入模式 InputStream inputStream = connection.getInputStream(); byte[] buffer = new byte[4096]; int bytesRead; long totalDownloaded = downloaded; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); totalDownloaded += bytesRead; } outputStream.close(); inputStream.close(); System.out.println("文件下载完成!"); } catch (IOException e) { e.printStackTrace(); } } } ``` 上述代码中,通过使用 HttpURLConnection 进行文件下载。首先检查文件是否存在,若存在则设置请求头 Range 属性为已下载文件的大小,从断点处开始下载。然后将文件输入流写入到文件输出流中,实现断点续传。 ### 回答3: Java断点续传代码主要通过处理文件的读写来实现。下面是一个简单的实现示例: ```java import java.io.*; public class ResumableFileDownloader { private static final int BUFFER_SIZE = 1024; public static void resumeDownload(String url, String filePath, long resumePosition) throws IOException { BufferedInputStream in = null; RandomAccessFile file = null; try { HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Range", "bytes=" + resumePosition + "-"); in = new BufferedInputStream(conn.getInputStream()); file = new RandomAccessFile(filePath, "rw"); file.seek(resumePosition); byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { file.write(buffer, 0, bytesRead); } } finally { if (in != null) { in.close(); } if (file != null) { file.close(); } } } public static void main(String[] args) { try { long resumePosition = 0; // 设置续传的起始位置 resumeDownload("http://example.com/file.pdf", "downloaded_file.pdf", resumePosition); } catch (IOException e) { e.printStackTrace(); } } } ``` 在该代码中,`resumeDownload`方法接收一个URL、文件保存路径和续传的起始位置作为参数。它首先从指定位置发起HTTP请求,设置`Range`头部以指定从该位置开始下载。然后通过`BufferedInputStream`读取数据,并通过`RandomAccessFile`写入文件,同时更新写入的位置。通过设置`RandomAccessFile`的`seek`方法,我们可以指定写入文件的位置。 在`main`方法中,我们可以设置续传的起始位置并调用`resumeDownload`方法来执行断点续传。例如,我们可以将文件下载到`downloaded_file.pdf`的位置,并从上一次未下载完成的位置继续下载。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值