原理:

断点续传的关键是断点,所以在制定传输协议的时候要设计好,如上图,我自定义了一个交互协议,每次下载请求都会带上下载的起始点,这样就可以支持从断点下载了,其实HTTP里的断点续传也是这个原理,在HTTP的头里有个可选的字段RANGE,表示下载的范围,下面是我用Java语言实现的下载断点续传示例。

提供下载的服务端代码:

  

         String path = "文件地址";
         BufferedInputStream bis = null;
         try {
             File file = new File(path);
             if (file.exists()) {
                 long p = 0L;
                 long toLength = 0L;
                 long contentLength = 0L;
                 int rangeSwitch = 0; // 0,从头开始的全文下载;1,从某字节开始的下载(bytes=27000-);2,从某字节开始到某字节结束的下载(bytes=27000-39000)
                 long fileLength;
                 String rangBytes = "";
                 fileLength = file.length();

                 // get file content
                 InputStream ins = new FileInputStream(file);
                 bis = new BufferedInputStream(ins);
//                 String d = new SimpleDateFormat("EEE,d MMM yyyy hh:mm:ss 'GMT' ",Locale.US).format(new Date());
//                 System.out.println(d);
                 // tell the client to allow accept-ranges
                 response.reset();
                 response.setHeader("Accept-Ranges", "bytes");
                 /*DateTime dateTime = DateTime.now();
                 System.out.println(dateTime.toString("r"));*/
                 response.setHeader("Last-Modified","Sat, 27 Jul 2017 12:14:58 GMT");
                 /*response.setHeader("HTTP/1.0 206 Partial Content", "206");*/
                 // client requests a file block download start byte

                 String range = request.getHeader("Range");
                log.info("range : "+range);

                 if (range != null && range.trim().length() > 0 && !"null".equals(range)) {
                     response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);
                     rangBytes = range.replaceAll("bytes=", "");
                     if (rangBytes.endsWith("-")) {  // bytes=270000-
                         rangeSwitch = 1;
                         p = Long.parseLong(rangBytes.substring(0, rangBytes.indexOf("-")));
                         contentLength = fileLength - p;  // 客户端请求的是270000之后的字节(包括bytes下标索引为270000的字节)
                     } else { // bytes=270000-320000
                         rangeSwitch = 2;
                         String temp1 = rangBytes.substring(0, rangBytes.indexOf("-"));
                         String temp2 = rangBytes.substring(rangBytes.indexOf("-") + 1, rangBytes.length());
                         p = Long.parseLong(temp1);
                         toLength = Long.parseLong(temp2);
                         contentLength = toLength - p + 1; // 客户端请求的是 270000-320000 之间的字节
                     }
                 } else {
                     contentLength = fileLength;
                 }

                 // 如果设设置了Content-Length,则客户端会自动进行多线程下载。如果不希望支持多线程,则不要设置这个参数。
                 // Content-Length: [文件的总大小] - [客户端请求的下载的文件块的开始字节]
                 response.setHeader("Content-Length", new Long(contentLength).toString());

                 // 断点开始
                 // 响应的格式是:
                 // Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]
                 if (rangeSwitch == 1) {
                     String contentRange = new StringBuffer("bytes ").append(new Long(p).toString()).append("-")
                             .append(new Long(fileLength - 1).toString()).append("/")
                             .append(new Long(fileLength).toString()).toString();
                     response.setHeader("Content-Range", contentRange);
                     bis.skip(p);
                 } else if (rangeSwitch == 2) {
                     String contentRange = range.replace("=", " ") + "/" + new Long(fileLength).toString();
                     response.setHeader("Content-Range", contentRange);
                     bis.skip(p);
                 } else {
                     String contentRange = new StringBuffer("bytes ").append("0-")
                             .append(fileLength - 1).append("/")
                             .append(fileLength).toString();
                     response.setHeader("Content-Range", contentRange);
                 }

                 String fileName = file.getName();
                 response.setContentType("application/octet-stream");
                 response.addHeader("Content-Disposition", "attachment;filename=" + fileName);

                 OutputStream out = response.getOutputStream();

                 BufferedOutputStream buf=new BufferedOutputStream(out);
                 int n = 0;
                 long readLength = 0;
                 int bsize = 1024;
                 byte[] bytes = new byte[bsize];
                 if (rangeSwitch == 2) {
                     // 针对 bytes=27000-39000 的请求,从27000开始写数据
                     while (readLength <= contentLength - bsize) {
                         n = bis.read(bytes);
                         readLength += n;
                         buf.write(bytes, 0, n);
                     }
                     if (readLength <= contentLength) {
                         n = bis.read(bytes, 0, (int) (contentLength - readLength));
                         buf.write(bytes, 0, n);
                     }
                 } else {
                     while ((n = bis.read(bytes)) != -1) {
                         buf.write(bytes,0,n);
                     }
                 }
                 buf.flush();
                 buf.close();
                 out.close();
                 bis.close();
             } else {
                 if (log.isDebugEnabled()) {
                     log.debug("Error: file " + path + " not found.");
                 }
             }
         } catch (IOException ie) {
             // 忽略 ClientAbortException 之类的异常
         } catch (Exception e) {
             log.error(e.getMessage());
             e.printStackTrace();
         }

下载的客户端代码:

  

    /** 
     *  request:get0startIndex0 
     *  response:fileLength0fileBinaryStream 
     *   
     * @param filepath 
     * @throws Exception 
     */  
    public void Get(String filepath) throws Exception {  
        Socket socket = new Socket();  
        // 建立连接  
        socket.connect(new InetSocketAddress("127.0.0.1", 8888));  
        // 获取网络流  
        OutputStream out = socket.getOutputStream();  
        InputStream in = socket.getInputStream();  
        // 文件传输协定命令  
        byte[] cmd = "get".getBytes();  
        out.write(cmd);  
        out.write(0);// 分隔符  
        int startIndex = 0;  
        // 要发送的文件  
        File file = new File(filepath);  
        if(file.exists()){  
            startIndex = (int) file.length();  
        }  
        System.out.println("Client startIndex : " + startIndex);  
        // 文件写出流  
        RandomAccessFile access = new RandomAccessFile(file,"rw");  
        // 断点  
        out.write(String.valueOf(startIndex).getBytes());  
        out.write(0);  
        out.flush();  
        // 文件长度  
        int temp = 0;  
        StringWriter sw = new StringWriter();  
        while((temp = in.read()) != 0){  
            sw.write(temp);  
            sw.flush();  
        }  
        int length = Integer.parseInt(sw.toString());  
        System.out.println("Client fileLength : " + length);  
        // 二进制文件缓冲区  
        byte[] buffer = new byte[1024*10];  
        // 剩余要读取的长度  
        int tatol = length - startIndex;  
        //  
        access.skipBytes(startIndex);  
        while (true) {  
            // 如果剩余长度为0则结束  
            if (tatol == 0) {  
                break;  
            }  
            // 本次要读取的长度假设为剩余长度  
            int len = tatol;  
            // 如果本次要读取的长度大于缓冲区的容量  
            if (len > buffer.length) {  
                // 修改本次要读取的长度为缓冲区的容量  
                len = buffer.length;  
            }  
            // 读取文件,返回真正读取的长度  
            int rlength = in.read(buffer, 0, len);  
            // 将剩余要读取的长度减去本次已经读取的  
            tatol -= rlength;  
            // 如果本次读取个数不为0则写入输出流,否则结束  
            if (rlength > 0) {  
                // 将本次读取的写入输出流中  
                access.write(buffer, 0, rlength);  
            } else {  
                break;  
            }  
            System.out.println("finish : " + ((float)(length -tatol) / length) *100 + " %");  
        }  
        System.out.println("finished!");  
        // 关闭流  
        access.close();  
        out.close();  
        in.close();  
    }  
  
    public static void main(String[] args) {  
        FTPClient client = new FTPClient();  
        try {  
            client.Get("E:\\ceshi\\test\\mm.pdf");  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  

代码直接copy过去就可以了  服务端的代码需要改下url

Java实现断点续传的更多相关文章

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

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

  2. java 实现断点续传

    请求头一:>>>>>>>>>>>>>>>>>>>>>>>> ...

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

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

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

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

  5. Java ftp断点续传

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

  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. Ubuntu安装Sublime Text 2

    参考资料:http://www.technoreply.com/how-to-install-sublime-text-2-on-ubuntu-12-04-unity/ 1.去Sublime Text ...

  2. pycharm设置安装python第三方插件

    pycharm设置安装python第三方插件 转载于:https://www.mindg.cn/?p=80 今天下了一个pycharm,设置用它来安装python插件,以下是安装步骤,与大家分享,我的 ...

  3. NFC读卡APP

    # 设计文档 ### 简介----------------------------- 这个APP的功能是使用手机的NFC读卡器功能,做到读取卡片支持M1卡和CPU卡. ### 功能列表-------- ...

  4. 基于HTML5 Canvas 实现的 Loading 效果

    Sonic.js 是一个很小的 JavaScript 类,用于创建基于 HTML5 画布的加载图像.更强大的是 Sonic.js 还提供了基于现成的例子的创建工具,可以帮助你实现更多自定义的(Load ...

  5. keep learning

    fueling people’s creativity  助长了人们的创造力,燃烧了人们的激情

  6. 10 部署应用程序和applet

    跳过 09 Swing用户界面组件 JAR文件 在将应用程序进行打包时, 使用者一定希望仅提供给其一个单独的文件, 而不是一个含有大量类文件的目录,  Java归档(JAR)文件就是为此目的而设计的. ...

  7. 数论 + 扩展欧几里得 - SGU 106. The equation

    The equation Problem's Link Mean: 给你7个数,a,b,c,x1,x2,y1,y2.求满足a*x+b*y=-c的解x满足x1<=x<=x2,y满足y1< ...

  8. post 获取checkbox值

    $str_tag=""; $s=$_POST['goods_server_name']; for($i=0;$i<count($s);$i++){ if($i==0){ $s ...

  9. 10个 jQuery 小技巧

    10个 jQuery 小技巧 -----整理by: xiaoshuai 1. 返回顶部按钮 可以利用 animate 和 scrollTop 来实现返回顶部的动画,而不需要使用其他插件. // Bac ...

  10. JavaScript重载

    在Javascript 中,每个函数都有一个隐含的对象arguments,表示给函数 实际传给的参数 ,那么我们可以用 arguments来实现函数的重载 <!DOCTYPE html PUBL ...