閱讀932 返回首頁    go 技術社區[雲棲]


java實現斷點續傳的原理

其實斷點續傳的原理很簡單,就是在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:// 連接的過程監視: 
	   藍色部分為: 客戶端請求 
	   紫色部分為: 服務器端響應 
	   如圖: 
	   https://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="<?xml version=/"1.0/" encoding=/"utf-8/" ?>"
    +"<xmlRequest>"
    +"<header>04</header>"
    +"<body>"
    +"<user userName=/"222/" password=/"222222/" currentVision=/"20101020165216/" >"
    +"</user>"
    +"</body>"
    +"</xmlRequest>";
*/
   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 = "https://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();
  }
 }
}


最後更新:2017-04-04 07:03:36

  上一篇:go MainWindow.xib
  下一篇:go java中Junit單元測試