Java 服务器端支持断点续传的源代码【支持快车、迅雷】
来源:互联网 发布:lcd1602编程 编辑:程序博客网 时间:2024/06/05 10:23
网上关于 Java 支持 HTTP 断点续传的文章不少,但关于 Java 服务器端支持 HTTP 断点续传的却比较少。
本文是 Java 服务器端支持 HTTP 断点续传的源代码,支持快车、迅雷。
快车和迅雷的 HTTP 下载有什么不同呢?快车的 HTTP 请求的 Range 内容形如“bytes=969998336-”,而迅雷的 HTTP 请求的 Range 内容则形如“bytes=1275856879-1275877358”。
本文使用一个简单的 Servlet 来作为支持断点续传的下载示例,在 Java Web 项目下部署好后,可以使用诸如 http://localhost/cds/http 的链接来调用 Servlet,进而被快车/迅雷监听进行下载。
本文是 Java 服务器端支持 HTTP 断点续传的源代码,支持快车、迅雷。
快车和迅雷的 HTTP 下载有什么不同呢?快车的 HTTP 请求的 Range 内容形如“bytes=969998336-”,而迅雷的 HTTP 请求的 Range 内容则形如“bytes=1275856879-1275877358”。
本文使用一个简单的 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 ArcSyncHttpDownloadServletextends HttpServlet {
- private staticfinal 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;//记录已下载文件大小
- boolean isFlashGet =true;//是否快车下载,迅雷的话为false
- long lenThun = 0;//记录迅雷需要下载的范围(比如bytes=27000-39000,则这个值是为39000)
- 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-
- isFlashGet = true;//快车下载
- rangeBytes = rangeBytes.substring(0, rangeBytes.indexOf('-'));
- pastLength = Long.parseLong(rangeBytes.trim());
- } else {//bytes=1275856879-1275877358
- isFlashGet = false;//迅雷下载
- 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 个字节开始下载
- lenThun = Long.parseLong(temp2);//bytes=1275856879-1275877358,到第 1275877358 个字节结束
- }
- }
- /**
- * 如果设设置了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("----------------------------不是从开始进行下载!服务器即将开始断点续传...");
- if (isFlashGet) {
- 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);
- } else {
- String contentRange = rangeBytes + "/" + new Long(fileLength).toString();
- response.setHeader("Content-Range", contentRange);
- }
- } 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(fileLength));
- os = response.getOutputStream();
- out = new BufferedOutputStream(os);
- raf = new RandomAccessFile(downloadFile,"r");
- try {
- if (isFlashGet) {//支持快车等 HTTP 断点续传客户端工具
- raf.seek(pastLength);//形如 bytes=969998336- 的客户端请求(如快车),跳过 969998336 个字节
- int n = 0;
- while ((n = raf.read(b,0, 1024)) != -1) {
- out.write(b, 0, n);
- }
- } else {//支持迅雷等 HTTP 断点续传客户端工具
- raf.seek(pastLength);//形如 bytes=1275856879-1275877358 的客户端请求(如迅雷),找到第 1275856879 个字节
- while (raf.getFilePointer() < lenThun) {
- out.write(raf.read());
- }
- }
- 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(#=@)。
- Java 服务器端支持断点续传的源代码【支持快车、迅雷】
- Java 服务器端支持断点续传的源代码【支持快车、迅雷】
- Java 服务器端支持断点续传的源代码【支持快车、迅雷】
- Java 服务器端支持断点续传的源代码【支持快车、迅雷】
- Java 服务器端支持断点续传的源代码【支持快车、迅雷】
- Java 服务器端支持断点续传的源代码【支持快车、迅雷】
- Java 服务器端支持断点续传的源代码【支持快车、迅雷】(仅支持 HTTP 协议)
- 使java服务器端支持断点续传
- Java服务器端支持断点续传代码
- Java服务器端支持断点续传编码实战
- java后台断点续传 支持html5 video及迅雷工具
- 自己写的一个简单的迅雷下载支持断点续传
- 下载地址转换器(支持 迅雷,旋风,快车,Rayfile...)
- Java实现服务器端动态流断点续传下载支持
- Java Spring Boot 服务器端断点续传功能支持 实现代码
- java 文件上传支持断点续传
- Java 下载支持断点续传服务端
- java下载工具类,支持断点续传
- linux用户权限的管理
- wamcc:将Prolog编译成C (No.7-5)
- android之组件5
- 第一篇博客:网站中用到的日历
- AndroidUI组件4- ProgressBar、SeekBar、ImageView、TabHost
- Java 服务器端支持断点续传的源代码【支持快车、迅雷】
- AndroidGUI02:EditText的常用技巧
- AndroidGUI03:Spinner的常用技巧
- 做长尾关键词:页面的整体优化才是基础
- 《Python基础教程》学习笔记之[D6]抽象之函数定义,参数
- linux下网络工具之tcpdump精析
- Ubuntu 9.10上建立ARM-Linux交叉编译环境
- Google的Native Client微笑着抽了HTML5一记漂亮的耳光
- js中innerHTML与innerText的用法与区别