`

Java 服务器端支持断点续传的源代码

 
阅读更多
Java 服务器端支持断点续传的源代码【支持快车、迅雷】(仅支持 HTTP 协议)

        网上关于 Java 支持 HTTP 断点续传的文章不少,但关于 Java 服务器端支持 HTTP 断点续传的却比较少。
        本文是 Java 服务器端支持 HTTP 断点续传的源代码,支持快车、迅雷。
        本文使用一个简单的 Servlet 来作为支持断点续传的下载示例,在 Java Web 项目下部署好后,可以使用诸如 http://localhost/cds/http 的链接来调用 Servlet,进而被快车/迅雷监听进行下载。
        ArcSyncHttpDownloadServlet 源代码:
[java] view plaincopyprint?
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); 
     
    @Override 
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException { 
        this.doPost(req, resp); 
    } 
     
    @Override 
    protected 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-1275877358 
                rangeSwitch = 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: bytes 
        response.setHeader("Accept-Ranges", "bytes");//如果是第一次下,还没有断点续传,状态是默认的 200,无需显式设置;响应的格式是:HTTP/1.1 200 OK 
        if (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 配置清单:
[html] view plaincopyprint?
    <!-- 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 源代码:
[java] view plaincopyprint?
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; 
    } 
     
     

分享到:
评论

相关推荐

    基于java的多线程断点续传程序源代码

    Java是一种广泛使用的编程语言,尤其在开发服务器端应用和网络...通过阅读和分析这些源代码,你可以更深入地理解Java多线程和断点续传技术的具体实现细节。这个项目对于学习Java网络编程和多线程控制是很好的实践案例。

    java大文件分块上传断点续传demo

    本示例"java大文件分块上传断点续传demo"提供了一个完整的解决方案,它允许用户将大文件分块上传,并在上传过程中支持断点续传。 首先,理解"分块上传"的概念。分块上传是将一个大文件分割成多个小块,然后逐个上传...

    Java实现断点续传

    “LoadClient.java”文件则可能是客户端的Java源代码,实现了断点续传的逻辑。这个类可能包含了文件分块、状态记录、重试机制等功能。客户端首先会检测本地是否有之前未完成的上传记录,如果有,它会从这个记录开始...

    数据同步断点续传源代码

    这种应用可能包含服务器端的代码,用于处理数据同步和断点续传的逻辑。例如,服务器端的组件可能会监控数据库的变化,当有新的数据时,通过HTTP或HTTPS协议将这些变化推送到客户端,同时使用断点续传技术来高效地...

    fastDFS断点续传实例

    - **源代码**:包括Java源文件,实现了FastDFS的客户端和服务器端接口,以及断点续传的逻辑。 - **配置文件**:如`fastdfs_client.conf`,用于配置FastDFS客户端连接参数。 - **测试用例**:可能包含单元测试或...

    JAVA大文件断点续传示例

    从提供的压缩包文件名`java-large-file-uploader-demo-master`来看,这可能是一个完整的Java项目,包含了实现上述功能的源代码、测试用例和相关文档。开发者可以通过查看和学习这个示例代码,了解如何在实际项目中...

    用Java实现断点续传(HTTP)

    断点续传主要涉及两个关键部分:客户端和服务器端。客户端负责保存已下载的数据状态,当网络中断后,可以继续从上次断开的位置开始下载;服务器端则需要支持接收客户端的请求,并能按需提供文件的部分内容。 1. **...

    断点续传实例

    在给定的压缩包文件中,虽然没有提供具体的源代码,但我们可以根据标题和描述推测,这个实例可能包含了一个用Java实现的断点续传功能。下面将详细介绍断点续传的基本原理以及可能涉及的关键技术。 1. 断点续传原理...

    java 文件上传下载 断点续传 断点上传

    本文将详细介绍如何在Java中实现文件的上传与下载,并重点讨论断点续传和断点上传的技术实现。 首先,让我们从基础开始。在Web端进行文件上传通常涉及到HTML表单、Servlet或Spring MVC等技术。在HTML中,可以使用`...

    http 断点续传演示

    服务器端收到这个请求后,如果支持断点续传,会在响应中返回`206 Partial Content`状态码,并在`Content-Range`响应头中包含完整的文件范围和当前传输的字节范围。例如,`Content-Range: bytes 5MB-10MB/20MB`表示总...

    断点续传.zip

    这个名为"断点续传.zip"的压缩包文件显然包含了与Java开发相关的代码示例,旨在帮助开发者理解和实现断点续传功能。这个功能允许用户在文件传输中断后,从上次中断的地方继续上传或下载,而不是重新开始整个过程,极...

    网页版大附件断点续传控件

    文件名“jupload”可能是指JUpload,这是一个知名的Java Applet大文件上传组件,支持断点续传、多文件选择、进度条显示等功能。它的源码可以作为学习和开发类似控件的参考。 总的来说,网页版大附件断点续传控件...

    Flex 利用socket断点续传

    2. Java服务器端代码:处理Socket连接,接收客户端的文件状态信息,返回剩余文件数据。 3. 可能还包含配置文件,如XML或properties文件,用于设置Socket连接参数、服务器地址等。 为了确保程序的稳定性和性能,...

    duandianxuchuan.rar_传 文件_断点续传

    【描述】提到的"用Java实现的断点续传源代码",意味着开发者使用Java编程语言编写了一段能够处理这种功能的程序。在Java中实现断点续传,通常涉及到以下几个关键知识点: 1. **文件操作**:Java的`java.io`包提供了...

    飞鸽传书JAVA源代码

    源代码中,开发者可能会利用这两个类创建服务器端和客户端,进行数据的收发,实现双向通信。 文件传输方面,JAVA的I/O流体系扮演了重要角色。FileInputStream和FileOutputStream用于读写文件,Socket的...

    fileUploadDemo-master_大文件上传_springboot_断点续传_

    通过研究源代码,我们可以学习到如何将Webupload与SpringBoot整合,以及如何在后端实现断点续传的逻辑。这对于我们理解和实现实现大文件上传具有重要的参考价值。 总之,大文件上传和断点续传是现代Web应用不可或缺...

    Android文件下载Demo(可断点续传)

    本示例"Android文件下载Demo(可断点续传)"提供了一个实现多线程下载并且支持断点续传功能的源代码DEMO。下面将详细解释这个Demo中涉及的关键知识点。 1. **多线程下载**: 在Android中,为了提高文件下载速度,...

    一种基于原生系统的下载demo支持断点续传支持多线程下载.rar

    5. 文件结构分析:在压缩包中的"一种基于原生系统的下载demo,支持断点续传,支持多线程下载"这个文件可能是源代码或者一个可执行的示例程序。它可能包含以下组成部分:主程序文件、配置文件、资源文件以及用于存储...

    断点续传。

    4. **服务器端处理**:服务器端需要支持对Range头的处理,根据请求返回特定范围的数据。对于不支持断点续传的服务器,可能需要额外的中间件或者自定义服务来实现。 5. **文件存储管理**:在客户端,需要有一个机制...

    断点续传demo,亲测可用,完整的demo

    - `src`:源代码目录,包含实现断点续传逻辑的Java或其他编程语言的源文件,如`FileTransferService`、`ChunkDownloader`等类。 - `doc`:可能包含API文档或开发者指南,解释了如何使用提供的断点续传功能。 - `....

Global site tag (gtag.js) - Google Analytics