Java断点续传服务器代码

  1. /** 
  2.  * 执行下载 
  3.  *  
  4.  * @param request 
  5.  * @param response 
  6.  */  
  7. protected void doDownload(HttpServletRequest request,  
  8.         HttpServletResponse response) throws Exception {  
  9.     File downloadFile = new File(resourceFullPath);// 要下载的文件  
  10.   
  11.     String extName = resourceFullPath.substring(resourceFullPath  
  12.             .lastIndexOf('.'));  
  13.     // request.getHeaderNames()  
  14.     // 文件不存在  
  15.     if (!downloadFile.exists()) {  
  16.         response.sendError(HttpServletResponse.SC_NOT_FOUND);  
  17.         return;  
  18.     }  
  19.   
  20.     if (cacheExts.contains(extName)) {// 被缓存的文件格式  
  21.         // 从缓存中读取文件  
  22.         downloadByCache(downloadFile, request, response);  
  23.         return;  
  24.     }  
  25.   
  26.     long fileLength = downloadFile.length();// 记录文件大小  
  27.     long pastLength = 0;// 记录已下载文件大小  
  28.     int rangeSwitch = 0;// 0:从头开始的全文下载;1:从某字节开始的下载(bytes=27000-);2:从某字节开始到某字节结束的下载(bytes=27000-39000)  
  29.     long toLength = 0;// 记录客户端需要下载的字节段的最后一个字节偏移量(比如bytes=27000-39000,则这个值是为39000)  
  30.     long contentLength = 0;// 客户端请求的字节总量  
  31.     String rangeBytes = "";// 记录客户端传来的形如“bytes=27000-”或者“bytes=27000-39000”的内容  
  32.     RandomAccessFile raf = null;// 负责读取数据  
  33.     OutputStream os = null;// 写出数据  
  34.     OutputStream out = null;// 缓冲  
  35.     int bsize = 1024;// 缓冲区大小  
  36.     byte b[] = new byte[bsize];// 暂存容器  
  37.   
  38.     if (request.getParameter("showheader") != null) {  
  39.         Enumeration paramNames = request.getHeaderNames();  
  40.   
  41.         while (paramNames.hasMoreElements()) {  
  42.             String name = paramNames.nextElement().toString();  
  43.             if (name != null && name.length() > 0) {  
  44.                 String value = request.getHeader(name);  
  45.                 logger.info("************" + name + ":" + value);  
  46.             }  
  47.         }  
  48.     }  
  49.     String range = request.getHeader("Range");  
  50.     // if(range == null)  
  51.     // range = "bytes=0-";  
  52.     int responseStatus = 206;  
  53.     if (range != null && range.trim().length() > 0 && !"null".equals(range)) {// 客户端请求的下载的文件块的开始字节  
  54.         responseStatus = javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT;  
  55.         logger.info("request.getHeader(\"Range\")=" + range);  
  56.         rangeBytes = range.replaceAll("bytes=""");  
  57.         if (rangeBytes.endsWith("-")) {// bytes=969998336-  
  58.             rangeSwitch = 1;  
  59.             rangeBytes = rangeBytes.substring(0, rangeBytes.indexOf('-'));  
  60.             pastLength = Long.parseLong(rangeBytes.trim());  
  61.             contentLength = fileLength - pastLength;// 客户端请求的是  
  62.                                                     // 969998336之后的字节(包括bytes下标索引为969998336的字节)  
  63.         } else {// bytes=1275856879-1275877358  
  64.             rangeSwitch = 2;  
  65.             String temp0 = rangeBytes.substring(0, rangeBytes.indexOf('-'));  
  66.             String temp2 = rangeBytes.substring(  
  67.                     rangeBytes.indexOf('-') + 1, rangeBytes.length());  
  68.             // bytes=1275856879-1275877358,从第1275856879个字节开始下载  
  69.             pastLength = Long.parseLong(temp0.trim());  
  70.             toLength = Long.parseLong(temp2);// bytes=1275856879-1275877358,到第  
  71.                                                 // 1275877358 个字节结束  
  72.             contentLength = toLength - pastLength + 1;// 客户端请求的是  
  73.                                                         // 1275856879-1275877358  
  74.                                                         // 之间的字节  
  75.         }  
  76.     } else {// 从开始进行下载  
  77.         contentLength = fileLength;// 客户端要求全文下载  
  78.     }  
  79.   
  80.     /** 
  81.      * 如果设设置了Content-Length,则客户端会自动进行多线程下载。如果不希望支持多线程,则不要设置这个参数。 响应的格式是: 
  82.      * Content-Length: [文件的总大小] - [客户端请求的下载的文件块的开始字节] 
  83.      * ServletActionContext.getResponse().setHeader("Content-Length", new 
  84.      * Long(file.length() - p).toString()); 
  85.      */  
  86.     // 来清除首部的空白行  
  87.     response.reset();  
  88.     // 告诉客户端允许断点续传多线程连接下载,响应的格式是:Accept-Ranges: bytes  
  89.     response.setHeader("Accept-Ranges""bytes");  
  90.     // 如果是第一次下,还没有断点续传,状态是默认的 200,无需显式设置;响应的格式是:HTTP/1.1  
  91.   
  92.     // response.addHeader("Cache-Control", "max-age=1296000");  
  93.     // response.addHeader("Expires", "Fri, 12 Oct 2012 03:43:01 GMT");  
  94.     // response.addHeader("Last-Modified", "Tue, 31 Jul 2012 03:58:36 GMT");  
  95.     // response.addHeader("Connection", "keep-alive");  
  96.     // response.addHeader("ETag", downloadFile.getName() + "-" +  
  97.     // downloadFile.lastModified());  
  98.     // response.addHeader("Last-Modified", "Thu, 27 Sep 2012 05:24:44 GMT");  
  99.   
  100.     if (rangeSwitch != 0) {  
  101.         response.setStatus(responseStatus);  
  102.         // 不是从最开始下载,断点下载响应号为206  
  103.         // 响应的格式是:  
  104.         // Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]  
  105.         logger.info("----------------------------片段下载,服务器即将开始断点续传...");  
  106.         switch (rangeSwitch) {  
  107.         case 1: {// 针对 bytes=27000- 的请求  
  108.             String contentRange = new StringBuffer("bytes ")  
  109.                     .append(new Long(pastLength).toString()).append("-")  
  110.                     .append(new Long(fileLength - 1).toString())  
  111.                     .append("/").append(new Long(fileLength).toString())  
  112.                     .toString();  
  113.             response.setHeader("Content-Range", contentRange);  
  114.             break;  
  115.         }  
  116.         case 2: {// 针对 bytes=27000-39000 的请求  
  117.             String contentRange = range.replace("="" ") + "/"  
  118.                     + new Long(fileLength).toString();  
  119.             response.setHeader("Content-Range", contentRange);  
  120.             break;  
  121.         }  
  122.         default: {  
  123.             break;  
  124.         }  
  125.         }  
  126.     } else {  
  127.         String contentRange = new StringBuffer("bytes ").append("0-")  
  128.                 .append(fileLength - 1).append("/").append(fileLength)  
  129.                 .toString();  
  130.         response.setHeader("Content-Range", contentRange);  
  131.         // 是从开始下载  
  132.         logger.info("----------------------------是从开始到最后完整下载!");  
  133.     }  
  134.   
  135.     try {  
  136.         // /设置文件Content-Type///  
  137.   
  138.         String contentType = contentTypeMap.get(extName);  
  139.   
  140.         if (contentType != null) {  
  141.             // /logger.debug("设置内容类型:" + contentType);  
  142.             response.setContentType(contentType);// set the MIME type.  
  143.         } else {  
  144.             response.setContentType("application/octet-stream");// set the  
  145.                                                                 // MIME  
  146.                                                                 // type.  
  147.         }  
  148.         // /设置文件下载名称Content-Disposition///  
  149.   
  150.         // 如果下载的文件需要重命名(因为物理文件存放的名称都是数字串,已在上传的时候被重命名过)  
  151.         if (StringUtils.isNotBlank(downloadFileName)) {  
  152.             String fileName = downloadFileName + extName;  
  153.             fileName = encodingFileName(StringUtils.trim(fileName), request);  
  154.             response.setHeader("Content-Disposition",  
  155.                     "attachment; filename=" + fileName);  
  156.         }  
  157.   
  158.         response.setHeader("Content-Length", String.valueOf(contentLength));  
  159.         os = response.getOutputStream();  
  160.         out = new BufferedOutputStream(os);  
  161.         raf = new RandomAccessFile(downloadFile, "r");  
  162.         try {  
  163.             long outLength = 0;// 实际输出字节数  
  164.             switch (rangeSwitch) {  
  165.             case 0: {// 普通下载,或者从头开始的下载  
  166.                 // 同1,没有break  
  167.             }  
  168.             case 1: {// 针对 bytes=27000- 的请求  
  169.                 raf.seek(pastLength);// 形如 bytes=969998336- 的客户端请求,跳过  
  170.                                         // 969998336 个字节  
  171.                 int n = 0;  
  172.                 while ((n = raf.read(b)) != -1) {  
  173.                     out.write(b, 0, n);  
  174.                     outLength += n;  
  175.                 }  
  176.                 // while ((n = raf.read(b, 0, 1024)) != -1) {  
  177.                 // out.write(b, 0, n);  
  178.                 // }  
  179.                 break;  
  180.             }  
  181.             case 2: {  
  182.                 // 针对 bytes=27000-39000 的请求,从27000开始写数据  
  183.                 raf.seek(pastLength);  
  184.                 int n = 0;  
  185.                 long readLength = 0;// 记录已读字节数  
  186.                 while (readLength <= contentLength - bsize) {// 大部分字节在这里读取  
  187.                     n = raf.read(b);  
  188.                     readLength += n;  
  189.                     out.write(b, 0, n);  
  190.                     outLength += n;  
  191.                 }  
  192.                 if (readLength <= contentLength) {// 余下的不足 1024 个字节在这里读取  
  193.                     n = raf.read(b, 0, (int) (contentLength - readLength));  
  194.                     out.write(b, 0, n);  
  195.                     outLength += n;  
  196.                 }  
  197.                 break;  
  198.             }  
  199.             default: {  
  200.                 break;  
  201.             }  
  202.             }  
  203.             logger.info("Content-Length为:" + contentLength + ";实际输出字节数:" + outLength);  
  204.             out.flush();  
  205.         } catch (IOException ie) {  
  206.             /** 
  207.              * 在写数据的时候, 对于 ClientAbortException 之类的异常, 
  208.              * 是因为客户端取消了下载,而服务器端继续向浏览器写入数据时, 抛出这个异常,这个是正常的。 
  209.              * 尤其是对于迅雷这种吸血的客户端软件, 明明已经有一个线程在读取 bytes=1275856879-1275877358, 
  210.              * 如果短时间内没有读取完毕,迅雷会再启第二个、第三个。。。线程来读取相同的字节段, 直到有一个线程读取完毕,迅雷会 KILL 
  211.              * 掉其他正在下载同一字节段的线程, 强行中止字节读出,造成服务器抛 ClientAbortException。 
  212.              * 所以,我们忽略这种异常 
  213.              */  
  214.             // ignore  
  215.         }  
  216.     } catch (Exception e) {  
  217.         logger.error(e.getMessage(), e);  
  218.     } finally {  
  219.         if (out != null) {  
  220.             try {  
  221.                 out.close();  
  222.             } catch (IOException e) {  
  223.                 logger.error(e.getMessage(), e);  
  224.             }  
  225.         }  
  226.         if (raf != null) {  
  227.             try {  
  228.                 raf.close();  
  229.             } catch (IOException e) {  
  230.                 logger.error(e.getMessage(), e);  
  231.             }  
  232.         }  
  233.     }  


原文地址::: http://blog.csdn.net/catoop/article/details/8022304



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java 断点续传代码实现可以分为以下几步: 1. 记录文件已经下载的长度 2. 设置 HTTP 请求头中 "Range" 字段,告诉服务器从文件的哪个位置开始下载 3. 使用 Java I/O 流下载文件 4. 保存文件已经下载的长度,以便下次继续下载 以下是一个简单的 Java 代码示例: ``` import java.io.*; import java.net.HttpURLConnection; import java.net.URL; public class Downloader { private static final int BUFFER_SIZE = 4096; public static void downloadFile(String fileURL, String saveDir) throws IOException { URL url = new URL(fileURL); HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); int responseCode = httpConn.getResponseCode(); // 检查 HTTP 响应码 if (responseCode == HttpURLConnection.HTTP_OK) { String fileName = ""; String disposition = httpConn.getHeaderField("Content-Disposition"); String contentType = httpConn.getContentType(); int contentLength = httpConn.getContentLength(); if (disposition != null) { // 检索文件名 int index = disposition.indexOf("filename="); if (index > 0) { fileName = disposition.substring(index + 10, disposition.length() - 1); } } else { // 检索文件名 fileName = fileURL.substring(fileURL.lastIndexOf("/") + 1, fileURL.length()); } System.out.println("Content-Type = " + contentType); System.out.println("Content-Disposition = " + disposition); System.out.println("Content-Length = " + contentLength); System.out.println("fileName = " + fileName); // 打开本地文件输出流 OutputStream outputStream = new FileOutputStream(saveDir + File.separator + fileName); // 打开网络输入流 InputStream inputStream = httpConn.getInputStream(); int bytesRead = -1; byte[] buffer = new byte[BUFFER_SIZE]; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0 ### 回答2: 断点续传是指在文件传输过程中,当传输中断时,重新开始传输时能够从中断点继续传输,而不是重新开始。下面给出一个使用 Java 实现断点续传的简单示例代码。 ```java import java.io.*; import java.net.HttpURLConnection; import java.net.URL; public class ResumeDownload { public static void main(String[] args) { String fileUrl = "http://example.com/file.zip"; // 需要下载的文件地址 String savePath = "C:/Downloads/file.zip"; // 文件保存路径 try { URL url = new URL(fileUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); File file = new File(savePath); long downloaded = 0; // 已下载的文件大小 if (file.exists()) { downloaded = file.length(); connection.setRequestProperty("Range", "bytes=" + downloaded + "-"); } connection.connect(); FileOutputStream outputStream = new FileOutputStream(file, true); // 追加写入模式 InputStream inputStream = connection.getInputStream(); byte[] buffer = new byte[4096]; int bytesRead; long totalDownloaded = downloaded; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); totalDownloaded += bytesRead; } outputStream.close(); inputStream.close(); System.out.println("文件下载完成!"); } catch (IOException e) { e.printStackTrace(); } } } ``` 上述代码中,通过使用 HttpURLConnection 进行文件下载。首先检查文件是否存在,若存在则设置请求头 Range 属性为已下载文件的大小,从断点处开始下载。然后将文件输入流写入到文件输出流中,实现断点续传。 ### 回答3: Java断点续传代码主要通过处理文件的读写来实现。下面是一个简单的实现示例: ```java import java.io.*; public class ResumableFileDownloader { private static final int BUFFER_SIZE = 1024; public static void resumeDownload(String url, String filePath, long resumePosition) throws IOException { BufferedInputStream in = null; RandomAccessFile file = null; try { HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Range", "bytes=" + resumePosition + "-"); in = new BufferedInputStream(conn.getInputStream()); file = new RandomAccessFile(filePath, "rw"); file.seek(resumePosition); byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { file.write(buffer, 0, bytesRead); } } finally { if (in != null) { in.close(); } if (file != null) { file.close(); } } } public static void main(String[] args) { try { long resumePosition = 0; // 设置续传的起始位置 resumeDownload("http://example.com/file.pdf", "downloaded_file.pdf", resumePosition); } catch (IOException e) { e.printStackTrace(); } } } ``` 在该代码中,`resumeDownload`方法接收一个URL、文件保存路径和续传的起始位置作为参数。它首先从指定位置发起HTTP请求,设置`Range`头部以指定从该位置开始下载。然后通过`BufferedInputStream`读取数据,并通过`RandomAccessFile`写入文件,同时更新写入的位置。通过设置`RandomAccessFile`的`seek`方法,我们可以指定写入文件的位置。 在`main`方法中,我们可以设置续传的起始位置并调用`resumeDownload`方法来执行断点续传。例如,我们可以将文件下载到`downloaded_file.pdf`的位置,并从上一次未下载完成的位置继续下载

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值