Java 伺服器端支援斷點續傳的原始碼【支援快車、迅雷】

來源:互聯網
上載者:User

Java 伺服器端支援斷點續傳的原始碼【支援快車、迅雷】(僅支援 HTTP 協議)

        網上關於 Java 支援 HTTP 斷點續傳的文章不少,但關於 Java 伺服器端支援 HTTP 斷點續傳的卻比較少。
        本文是 Java 伺服器端支援 HTTP 斷點續傳的原始碼,支援快車、迅雷。
        本文使用一個簡單的 Servlet 來作為支援斷點續傳的下載樣本,在 Java Web 項目下部署好後,可以使用諸如 http://localhost/cds/http 的連結來調用 Servlet,進而被快車/迅雷監聽進行下載。

        ArcSyncHttpDownloadServlet 原始碼:

package com.defonds.cds.common;import java.io.BufferedOutputStream;import java.io.File;import java.io.IOException;import java.io.OutputStream;import java.io.RandomAccessFile;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import com.defonds.cds.common.util.CommonUtil;//HTTP 斷點續傳 demo(用戶端測試載入器:快車、迅雷)public class ArcSyncHttpDownloadServlet extends HttpServlet {private static final long serialVersionUID = 1L;final static Log log = LogFactory.getLog(ArcSyncHttpDownloadServlet.class);@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {this.doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) {File downloadFile = new File("D:/defonds/book/pattern/SteveJobsZH.pdf");//要下載的檔案    long fileLength = downloadFile.length();//記錄檔案大小long pastLength = 0;//記錄已下載檔案大小int rangeSwitch = 0;//0:從頭開始的全文下載;1:從某位元組開始的下載(bytes=27000-);2:從某位元組開始到某位元組結束的下載(bytes=27000-39000)long toLength = 0;//記錄用戶端需要下載的位元組段的最後一個位元組位移量(比如bytes=27000-39000,則這個值是為39000)long contentLength = 0;//用戶端請求的位元組總量String rangeBytes = "";//記錄用戶端傳來的形如“bytes=27000-”或者“bytes=27000-39000”的內容 RandomAccessFile raf = null;//負責讀取資料OutputStream os = null;//寫出資料OutputStream out = null;//緩衝byte b[] = new byte[1024];//暫存容器if (request.getHeader("Range") != null) {// 用戶端請求的下載的檔案塊的開始位元組response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);log.info("request.getHeader(\"Range\")=" + request.getHeader("Range"));rangeBytes = request.getHeader("Range").replaceAll("bytes=", "");if (rangeBytes.indexOf('-') == rangeBytes.length() - 1) {//bytes=969998336-rangeSwitch = 1;rangeBytes = rangeBytes.substring(0, rangeBytes.indexOf('-'));pastLength = Long.parseLong(rangeBytes.trim());contentLength = fileLength - pastLength + 1;//用戶端請求的是 969998336 之後的位元組} else {//bytes=1275856879-1275877358rangeSwitch = 2;String temp0 = rangeBytes.substring(0,rangeBytes.indexOf('-'));String temp2 = rangeBytes.substring(rangeBytes.indexOf('-') + 1, rangeBytes.length());pastLength = Long.parseLong(temp0.trim());//bytes=1275856879-1275877358,從第 1275856879 個位元組開始下載toLength = Long.parseLong(temp2);//bytes=1275856879-1275877358,到第 1275877358 個位元組結束contentLength = toLength - pastLength + 1;//用戶端請求的是 1275856879-1275877358  之間的位元組}} else {//從開始進行下載contentLength = fileLength;//用戶端要求全文下載}/** * 如果設設定了Content-Length,則用戶端會自動進行多線程下載。如果不希望支援多線程,則不要設定這個參數。 * 響應的格式是: * Content-Length: [檔案的總大小] - [用戶端請求的下載的檔案塊的開始位元組] * ServletActionContext.getResponse().setHeader("Content-Length", * new Long(file.length() - p).toString()); */response.reset();//告訴用戶端允許斷點續傳多線程串連下載,響應的格式是:Accept-Ranges: bytesresponse.setHeader("Accept-Ranges", "bytes");//如果是第一次下,還沒有斷點續傳,狀態是預設的 200,無需顯式設定;響應的格式是:HTTP/1.1 200 OKif (pastLength != 0) {//不是從最開始下載,//響應的格式是://Content-Range: bytes [檔案塊的開始位元組]-[檔案的總大小 - 1]/[檔案的總大小]log.info("----------------------------不是從開始進行下載!伺服器即將開始斷點續傳...");switch (rangeSwitch) {case 1 : {//針對 bytes=27000- 的請求String contentRange = new StringBuffer("bytes ").append(new Long(pastLength).toString()).append("-").append(new Long(fileLength - 1).toString()).append("/").append(new Long(fileLength).toString()).toString();response.setHeader("Content-Range", contentRange);break;}case 2 : {//針對 bytes=27000-39000 的請求String contentRange = rangeBytes + "/" + new Long(fileLength).toString();response.setHeader("Content-Range", contentRange);break;}default : {break;}}} else {//是從開始下載log.info("----------------------------是從開始進行下載!");}try {    response.addHeader("Content-Disposition", "attachment; filename=\"" + downloadFile.getName() + "\"");    response.setContentType( CommonUtil.setContentType(downloadFile.getName()));// set the MIME type.        response.addHeader("Content-Length", String.valueOf(contentLength));os = response.getOutputStream();    out = new BufferedOutputStream(os);    raf = new RandomAccessFile(downloadFile, "r");    try {    switch (rangeSwitch) {    case 0 : {//普通下載,或者從頭開始的下載    //同1    }    case 1 : {//針對 bytes=27000- 的請求    raf.seek(pastLength);//形如 bytes=969998336- 的用戶端請求,跳過 969998336  個位元組        int n = 0;        while ((n = raf.read(b, 0, 1024)) != -1) {        out.write(b, 0, n);        }    break;    }    case 2 : {//針對 bytes=27000-39000 的請求    raf.seek(pastLength - 1);//形如 bytes=1275856879-1275877358 的用戶端請求,找到第 1275856879 個位元組    int n = 0;    long readLength = 0;//記錄已讀位元組數    while (readLength <= contentLength - 1024) {//大部分位元組在這裡讀取    n = raf.read(b, 0, 1024);    readLength += 1024;    out.write(b, 0, n);    }    if (readLength <= contentLength) {//餘下的不足 1024 個位元組在這裡讀取    n = raf.read(b, 0, (int)(contentLength - readLength));    out.write(b, 0, n);    }//    //    raf.seek(pastLength);//形如 bytes=1275856879-1275877358 的用戶端請求,找到第 1275856879 個位元組//        while (raf.getFilePointer() < toLength) {//        out.write(raf.read());//        }    break;    }    default : {    break;    }    }        out.flush();    } catch(IOException ie) {    /** * 在寫資料的時候, * 對於 ClientAbortException 之類的異常, * 是因為用戶端取消了下載,而伺服器端繼續向瀏覽器寫入資料時, * 拋出這個異常,這個是正常的。 * 尤其是對於迅雷這種吸血的用戶端軟體, * 明明已經有一個線程在讀取 bytes=1275856879-1275877358, * 如果短時間內沒有讀取完畢,迅雷會再啟第二個、第三個。。。線程來讀取相同的位元組段, * 直到有一個線程讀取完畢,迅雷會 KILL 掉其他正在下載同一位元組段的線程, * 強行中止位元組讀出,造成伺服器拋 ClientAbortException。 * 所以,我們忽略這種異常 *///ignore    }} catch (Exception e) {log.error(e.getMessage(), e);} finally {if (out != null) {try {out.close();} catch (IOException e) {log.error(e.getMessage(), e);}}if (raf != null) {try {raf.close();} catch (IOException e) {log.error(e.getMessage(), e);}}}}}

        ArcSyncHttpDownloadServlet 的 web.xml 配置清單:

<!-- HTTP 斷點續傳 demo:127.0.0.1/cds/http --><servlet><servlet-name>httpServlet</servlet-name><servlet-class>com.defonds.cds.common.ArcSyncHttpDownloadServlet</servlet-class></servlet><servlet-mapping><servlet-name>httpServlet</servlet-name><url-pattern>/http</url-pattern></servlet-mapping>

        ArcSyncHttpDownloadServlet 調用到的工具類 com.defonds.cds.common.util.CommonUtil 原始碼:

package com.defonds.cds.common.util;public class CommonUtil {public static String setContentType(String returnFileName){String contentType = "application/octet-stream";if (returnFileName.lastIndexOf(".") < 0)return contentType;returnFileName = returnFileName.toLowerCase();returnFileName = returnFileName.substring(returnFileName.lastIndexOf(".")+1);if (returnFileName.equals("html") || returnFileName.equals("htm") || returnFileName.equals("shtml")){contentType = "text/html";} else if (returnFileName.equals("css")){contentType = "text/css";} else if (returnFileName.equals("xml")){contentType = "text/xml";} else if (returnFileName.equals("gif")){contentType = "image/gif";} else if (returnFileName.equals("jpeg") || returnFileName.equals("jpg")){contentType = "image/jpeg";} else if (returnFileName.equals("js")){contentType = "application/x-javascript";} else if (returnFileName.equals("atom")){contentType = "application/atom+xml";} else if (returnFileName.equals("rss")){contentType = "application/rss+xml";} else if (returnFileName.equals("mml")){contentType = "text/mathml"; } else if (returnFileName.equals("txt")){contentType = "text/plain";} else if (returnFileName.equals("jad")){contentType = "text/vnd.sun.j2me.app-descriptor";} else if (returnFileName.equals("wml")){contentType = "text/vnd.wap.wml";} else if (returnFileName.equals("htc")){contentType = "text/x-component";} else if (returnFileName.equals("png")){contentType = "image/png";} else if (returnFileName.equals("tif") || returnFileName.equals("tiff")){contentType = "image/tiff";} else if (returnFileName.equals("wbmp")){contentType = "image/vnd.wap.wbmp";} else if (returnFileName.equals("ico")){contentType = "image/x-icon";} else if (returnFileName.equals("jng")){contentType = "image/x-jng";} else if (returnFileName.equals("bmp")){contentType = "image/x-ms-bmp";} else if (returnFileName.equals("svg")){contentType = "image/svg+xml";} else if (returnFileName.equals("jar") || returnFileName.equals("var") || returnFileName.equals("ear")){contentType = "application/java-archive";} else if (returnFileName.equals("doc")){contentType = "application/msword";} else if (returnFileName.equals("pdf")){contentType = "application/pdf";} else if (returnFileName.equals("rtf")){contentType = "application/rtf";} else if (returnFileName.equals("xls")){contentType = "application/vnd.ms-excel"; } else if (returnFileName.equals("ppt")){contentType = "application/vnd.ms-powerpoint";} else if (returnFileName.equals("7z")){contentType = "application/x-7z-compressed";} else if (returnFileName.equals("rar")){contentType = "application/x-rar-compressed";} else if (returnFileName.equals("swf")){contentType = "application/x-shockwave-flash";} else if (returnFileName.equals("rpm")){contentType = "application/x-redhat-package-manager";} else if (returnFileName.equals("der") || returnFileName.equals("pem") || returnFileName.equals("crt")){contentType = "application/x-x509-ca-cert";} else if (returnFileName.equals("xhtml")){contentType = "application/xhtml+xml";} else if (returnFileName.equals("zip")){contentType = "application/zip";} else if (returnFileName.equals("mid") || returnFileName.equals("midi") || returnFileName.equals("kar")){contentType = "audio/midi";} else if (returnFileName.equals("mp3")){contentType = "audio/mpeg";} else if (returnFileName.equals("ogg")){contentType = "audio/ogg";} else if (returnFileName.equals("m4a")){contentType = "audio/x-m4a";} else if (returnFileName.equals("ra")){contentType = "audio/x-realaudio";} else if (returnFileName.equals("3gpp") || returnFileName.equals("3gp")){contentType = "video/3gpp";} else if (returnFileName.equals("mp4") ){contentType = "video/mp4";} else if (returnFileName.equals("mpeg") || returnFileName.equals("mpg") ){contentType = "video/mpeg";} else if (returnFileName.equals("mov")){contentType = "video/quicktime";} else if (returnFileName.equals("flv")){contentType = "video/x-flv";} else if (returnFileName.equals("m4v")){contentType = "video/x-m4v";} else if (returnFileName.equals("mng")){contentType = "video/x-mng";} else if (returnFileName.equals("asx") || returnFileName.equals("asf")){contentType = "video/x-ms-asf";} else if (returnFileName.equals("wmv")){contentType = "video/x-ms-wmv";} else if (returnFileName.equals("avi")){contentType = "video/x-msvideo";}return contentType;}}

        本文範例程式碼在用戶端使用快車(FlashGet)3、迅雷7 進行斷點續傳的環境下測試 OK。

        本文僅為用來技術交流的示範 Demo,技術水平及知識面所限,其中錯誤和不當之處在所難免,敬請諒解。歡迎聯絡作者進行批評指正,技術交流 MSN:defonds#hotmail.com(#=@),也可以給我發郵件,email:defonds#163.com(#=@)。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.