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

/**
     * 下載服務器已存在的文件,支持斷點續傳
     *
     * @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();
            }
        }
    }

開發中遇到的一個錯誤提示:java

org.apache.catalina.connector.ClientAbortException: Connection reset by peer: socket write errorapache

該錯誤的緣由就是由於上面的Content-Length: 1024 與請求頭重請求的長度不一致,致使了請求端拒絕了編程

 

http斷點續傳原理:http頭 Range、Content-Range瀏覽器

所謂斷點續傳,也就是要從文件已經下載的地方開始繼續下載。在之前版本的 HTTP 協議是不支持斷點的,HTTP/1.1 開始就支持了。通常斷點下載時纔用到 Range 和 Content-Range 實體頭。服務器

Range 網絡

用於請求頭中,指定第一個字節的位置和最後一個字節的位置,通常格式:app

Range:(unit=first byte pos)-[last byte pos] dom

Content-Rangesocket

用於響應頭,指定整個實體中的一部分的插入位置,他也指示了整個實體的長度。在服務器向客戶返回一個部分響應,它必須描述響應覆蓋的範圍和整個實體長度。通常格式: 

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);
    }

怎麼樣,也很簡單吧。 接下來要作的就是整合成一個完整的程序了。包括一系列的線程控制等等。

注:轉載http://www.ibm.com/developerworks/cn/java/joy-down/index.html

相關文章
相關標籤/搜索