博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java实现断点续传的原理
阅读量:6767 次
发布时间:2019-06-26

本文共 6151 字,大约阅读时间需要 20 分钟。

  hot3.png

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

服务端代码:

/* 	   文件名可存为: Download.jsp 	   HTTP 协议的请求与响应的会话过程可通过使用 FlashGet 下载 Http:// 连接的过程监视: 	   蓝色部分为: 客户端请求 	   紫色部分为: 服务器端响应 	   如图: 	   http://blog.csdn.net/images/blog_csdn_net/playyuer/30110/o_FlashGet.gif 	   或参阅,后面的 FlashGet 会话列表: 	   	*/ 	  //你可以使用你服务器上的文件及其路径 	  String s = "I://SetupRes//Sun//j2re-1_4_2_05-windows-i586-p.exe"; 	  //String s = "e://tree.mdb"; 	  //经测试 RandomAccessFile 也可以实现,有兴趣可将注释去掉,并注释掉 FileInputStream 版本的语句 	  //java.io.RandomAccessFile raf = new java.io.RandomAccessFile(s,"r"); 	  java.io.File f = new java.io.File(s); 	  java.io.FileInputStream fis = new java.io.FileInputStream(f); 	  response.reset(); 	  response.setHeader("Server", "playyuer@Microshaoft.com"); 	  //告诉客户端允许断点续传多线程连接下载 	  //响应的格式是: 	  //Accept-Ranges: bytes 	  response.setHeader("Accept-Ranges", "bytes"); 	  long p = 0; 	  long l = 0; 	  //l = raf.length(); 	  l = f.length(); 	  //如果是第一次下,还没有断点续传,状态是默认的 200,无需显式设置 	  //响应的格式是: 	  //HTTP/1.1 200 OK 	  if (request.getHeader("Range") != null) //客户端请求的下载的文件块的开始字节 	  { 	   //如果是下载文件的范围而不是全部,向客户端声明支持并开始文件块下载 	   //要设置状态 	   //响应的格式是: 	   //HTTP/1.1 206 Partial Content 	   response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);//206 	   //从请求中得到开始的字节 	   //请求的格式是: 	   //Range: bytes=[文件块的开始字节]- 	   p = Long.parseLong(request.getHeader("Range").replaceAll("bytes=","").replaceAll("-","")); 	  } 	  //下载的文件(或块)长度 	  //响应的格式是: 	  //Content-Length: [文件的总大小] - [客户端请求的下载的文件块的开始字节] 	  response.setHeader("Content-Length", new Long(l - p).toString()); 	  if (p != 0) 	  { 	   //不是从最开始下载, 	   //响应的格式是: 	   //Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小] 	   response.setHeader("Content-Range","bytes " + new Long(p).toString() + "-" + new Long(l -1).toString() + "/" + new Long(l).toString()); 	  } 	  //response.setHeader("Connection", "Close"); //如果有此句话不能用 IE 直接下载 	  //使客户端直接下载 	  //响应的格式是: 	  //Content-Type: application/octet-stream 	  response.setContentType("application/octet-stream"); 	  //为客户端下载指定默认的下载文件名称 	  //响应的格式是: 	  //Content-Disposition: attachment;filename="[文件名]" 	  //response.setHeader("Content-Disposition", "attachment;filename=/"" + s.substring(s.lastIndexOf("//") + 1) + "/""); //经测试 RandomAccessFile 也可以实现,有兴趣可将注释去掉,并注释掉 FileInputStream 版本的语句 	  response.setHeader("Content-Disposition", "attachment;filename=/"" + f.getName() + "/""); 	  //raf.seek(p); 	  fis.skip(p); 	  byte[] b = new byte[1024]; 	  int i; 	  //while ( (i = raf.read(b)) != -1 ) //经测试 RandomAccessFile 也可以实现,有兴趣可将注释去掉,并注释掉 FileInputStream 版本的语句 	  while ( (i = fis.read(b)) != -1 ) 	  { 	   response.getOutputStream().write(b,0,i); 	  } 	  //raf.close();//经测试 RandomAccessFile 也可以实现,有兴趣可将注释去掉,并注释掉 FileInputStream 版本的语句 	  fis.close();
客户端测试代码:

public static void down(String URL,long nPos,String savePathAndFile){ HttpURLConnection conn =null; try{/*  String content="
" +"
" +"
04
" +"" +"
" +"
" +"" +"
";*/ conn = (HttpURLConnection)new URL(URL).openConnection();/* conn.setRequestProperty("content-type", "text/html"); conn.setRequestProperty("User-Agent", "NetFox");// 设置User-Agent conn.setRequestProperty("RANGE", "bytes=" + nPos);// 设置断点续传的开始位置 conn.setRequestMethod("POST"); //设置请求方法为POST, 也可以为GET conn.setDoInput(true); conn.setDoOutput(true); OutputStream outStream = conn.getOutputStream(); PrintWriter out = new PrintWriter(outStream); out.print(content); out.flush(); out.close();*/ // 获得输入流 InputStream input = conn.getInputStream(); RandomAccessFile oSavedFile = new RandomAccessFile(savePathAndFile, "rw"); // 定位文件指针到nPos位置 oSavedFile.seek(nPos); byte[] b = new byte[1024]; int nRead; // 从输入流中读入字节流,然后写到文件中 while ((nRead = input.read(b, 0, 1024)) > 0) { (oSavedFile).write(b, 0, nRead); } conn.disconnect(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }}public static void main(String[] args) { String url = "http://localhost:8181/ssoapp/clientRequest"; String savePath = "e://"; String fileName = "4.ppt"; String fileNam = fileName; HttpURLConnection conn = null; try { } catch (Exception e) { e.printStackTrace(); } File file = new File(savePath + fileName); int i = 0; if (file.exists()) { // 先看看是否是完整的,完整,换名字,跳出循环,不完整,继续下载 long localFileSize = file.length(); System.out.println("已有文件大小为:" + localFileSize); if (localFileSize >0) { System.out.println("文件续传"); down(url, localFileSize, savePath + fileName); } else { System.out.println("文件存在,重新下载"); down(url, 0, savePath + fileName); } } else { try { file.createNewFile(); System.out.println("下载中"); down(url, 0, savePath + fileName); } catch (IOException e) { e.printStackTrace(); } }}

转载于:https://my.oschina.net/hjaa/blog/699622

你可能感兴趣的文章
深度链接对社会化营销有哪些价值和作用?
查看>>
华为现神预判,为自己准备了5条强大的后援,拒绝再现中兴尬局
查看>>
强化学习01|“什么叫强化学习
查看>>
IT兄弟连 JavaWeb教程 AJAX定义以及解决的问题
查看>>
常用的linux查看主机命令
查看>>
android 资源
查看>>
我的友情链接
查看>>
Drupal7系统初步设置篇-Ubuntu 14.04 LTS
查看>>
4-3-word2003-word文件操作和视图设置
查看>>
DB2 常用命令大全【转】
查看>>
XenServer安装最佳实践
查看>>
centos6.4下Zabbix系列之Zabbix安装搭建及汉化
查看>>
PEOPLE CMM 第五日
查看>>
windows 2003 dhcp服务器,主机移动vlan获取原理ip
查看>>
Android 学习--ListView 的使用(四)
查看>>
js实现图片联动效果
查看>>
基于MDK编译器 STM32与12864液晶显示程序 和电路连接
查看>>
启动apache 提示命令不存在
查看>>
Mem系列函数与Str系列函数总结 (三) memset 与 strset
查看>>
编写五子棋游戏的趣事
查看>>