- 浏览: 2521 次
文章分类
最新评论
网上关于多线程下载的教程基本都是以请求某个具体的文件url路径为例子, 这种例子与实际应用不太相符。
一般我们都是通过请求一个Controller路径或servlet路径来下载文件内容:
这里就涉及到根据请求头rang返回客户端需要的字节数:
服务端代码:
客户端代码:
一般我们都是通过请求一个Controller路径或servlet路径来下载文件内容:
这里就涉及到根据请求头rang返回客户端需要的字节数:
服务端代码:
package org.wenchj.servlet; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.ArrayUtils; public class DownloadServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void download(HttpServletRequest request, HttpServletResponse response) { try { String filePath = request.getSession().getServletContext().getRealPath("/115com_v2.1.1.exe"); System.out.println(filePath); File f = new File(filePath); FileInputStream fis = new FileInputStream(f); response.reset(); // 告诉客户端允许断点续传多线程连接下载 // 响应的格式是: // Accept-Ranges: bytes response.setHeader("Accept-Ranges", "bytes"); long p = 0; long l = 0; l = f.length(); // 如果是第一次下,还没有断点续传,状态是默认的 200,无需显式设置 // 响应的格式是: // HTTP/1.1 200 OK if (request.getHeader("Range") != null) // 客户端请求的下载的文件块的开始字节 { // 如果是下载文件的范围而不是全部,向客户端声明支持并开始文件块下载 // 要设置状态 // 响应的格式是: // HTTP/1.1 206 Partial Content response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);// 206 // 从请求中得到开始的字节 // 请求的格式是: // Range: bytes=[文件块的开始字节]- p = Long.parseLong(request.getHeader("Range").replaceAll("bytes=", "").replaceAll("-", "")); } // 下载的文件(或块)长度 // 响应的格式是: // Content-Length: [文件的总大小] - [客户端请求的下载的文件块的开始字节] response.setHeader("Content-Length", new Long(l - p).toString()); if (p != 0) { // 不是从最开始下载, // 响应的格式是: // Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小] response.setHeader( "Content-Range", "bytes " + new Long(p).toString() + "-" + new Long(l - 1).toString() + "/" + new Long(l).toString()); } // response.setHeader("Connection", "Close"); //如果有此句话不能用 IE 直接下载 // 使客户端直接下载 // 响应的格式是: // Content-Type: application/octet-stream response.setContentType("application/octet-stream"); // 为客户端下载指定默认的下载文件名称 // 响应的格式是: // Content-Disposition: attachment;filename="[文件名]" // response.setHeader("Content-Disposition", // "attachment;filename=\"" + s.substring(s.lastIndexOf("\\") + 1) + // "\""); //经测试 RandomAccessFile 也可以实现,有兴趣可将注释去掉,并注释掉 // FileInputStream 版本的语句 // response.setHeader("Content-Disposition", "attachment;filename=\"" + app.getName() + ".apk\""); // 可选 setFileAttachmentHeader(response, f.getName()); // raf.seek(p); fis.skip(p); byte[] b = new byte[1024]; int i; // while ( (i = raf.read(b)) != -1 ) //经测试 RandomAccessFile // 也可以实现,有兴趣可将注释去掉,并注释掉 FileInputStream 版本的语句 while ((i = fis.read(b)) != -1) { response.getOutputStream().write(b, 0, i); } // raf.close();//经测试 RandomAccessFile 也可以实现,有兴趣可将注释去掉,并注释掉 // FileInputStream 版本的语句 fis.close(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { download2(request, response); // download2(request, response); } private void download2(HttpServletRequest request, HttpServletResponse response) throws IOException { // String fileName = "/smack_3_3_1.zip"; String fileName = "/115com_v2.1.1.exe"; String filePath = request.getSession().getServletContext().getRealPath(fileName); System.out.println("[" + Thread.currentThread().getName() + "]" + filePath); File f = new File(filePath); response.setHeader("Content-Length", String.valueOf(f.length())); // 可选 setFileAttachmentHeader(response, f.getName()); System.out.println("Range--->" + request.getHeader("Range")); byte[] content = FileUtils.readFileToByteArray(f); System.out.println("文件总长度content.length-->" + content.length); String rangeHeader = request.getHeader("Range"); if (rangeHeader != null) { String[] split = rangeHeader.split("="); String[] range = split[1].split("-"); int start = Integer.valueOf(range[0]); int end = Integer.valueOf(range[1]); System.out.println("start:" + start + ", end:" + end); byte[] temp = new byte[end - start]; // 截取数组 temp = ArrayUtils.subarray(content, start, end + 1); response.setHeader("Content-Length", String.valueOf(end - start)); responseFileContent(response, temp); } else { response.setHeader("Content-Length", String.valueOf(f.length())); responseFileContent(response, content); } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } /* * 返回文件内容 */ private void responseFileContent(HttpServletResponse response, byte[] content) { try { OutputStream outStream = response.getOutputStream(); outStream.write(content); outStream.flush(); outStream.close(); } catch (Exception e) { // e.printStackTrace(); } } /* * 以附件形式下载(用浏览器下载会弹出对话框) */ private static void setFileAttachmentHeader(HttpServletResponse response, String fileName) { try { String encodedFileName = new String(fileName.getBytes(), "ISO8859-1"); response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\""); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } }
客户端代码:
package org.wenchj.servlet; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; public class MulThreadDownloader { public static void main(String[] args) throws Exception { String path = "http://localhost:8088/download/DownloadServlet"; int threadsize = 3; new MulThreadDownloader().download(path, threadsize); } private void download(String path, int threadsize) throws Exception { URL downpath = new URL(path); HttpURLConnection conn = (HttpURLConnection) downpath.openConnection(); conn.setConnectTimeout(5000); conn.setRequestMethod("GET"); if (conn.getResponseCode() == 200) { int length = conn.getContentLength();// 获取网络文件的长度 System.out.println("获取网络文件的长度" + length); File file = new File("115com_v2.1.1.exe"); RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");// 生成本地文件 accessFile.setLength(length); accessFile.close(); // 计算每条线程负责下载的数据量 int block = (length % threadsize) == 0 ? length / threadsize : (length / threadsize) + 1; System.out.println("block:" + block); for (int threadid = 0; threadid < threadsize; threadid++) { new DownloadThread(threadid, downpath, block, file).start(); } } } // 负责下载操作 private final class DownloadThread extends Thread { private int threadid; private URL downpath; private int block; private File file; public DownloadThread(int threadid, URL downpath, int block, File file) { this.threadid = threadid; this.downpath = downpath; this.block = block; this.file = file; } @Override public void run() { // if (threadid == 0) { // try { // System.out.println("线程" + threadid + "睡眠20s..."); // Thread.sleep(20 * 1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } // } int startposition = threadid * block;// 从网络文件的什么位置开始下载数据 int endposition = ((threadid + 1) * block) - 1;// 下载到网络文件的什么位置结束 // 指示该线程要从网络文件的startposition位置开始下载,下载到endposition位置结束 // Range:bytes=startposition-endposition try { RandomAccessFile accessFile = new RandomAccessFile(file, "rwd"); accessFile.seek(startposition);// 移动指针到文件的某个位置 HttpURLConnection conn = (HttpURLConnection) downpath.openConnection(); conn.setConnectTimeout(5000); conn.setRequestMethod("GET"); conn.setRequestProperty("Range", "bytes=" + startposition + "-" + endposition); InputStream inStream = conn.getInputStream(); System.out.println(Thread.currentThread().getName() + ", ContentLength:" + conn.getContentLength()); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { accessFile.write(buffer, 0, len); } accessFile.close(); inStream.close(); System.out.println("线程" + (threadid) + "下载完成"); } catch (Exception e) { e.printStackTrace(); } } } }
相关推荐
多线程下载则能显著提高大文件下载的速度,通过将文件分割成多个部分并同时下载来利用多核处理器的优势。 这个C# DIY HttpWebClient工具类基于对System.Net.WebClient的修改和扩展。WebClient是.NET Framework提供...
在本项目中,“易语言多线程下载不卡速度快”是一个利用易语言实现的多线程下载工具,旨在提供高效、稳定的文件下载体验。通过多线程技术,该工具能够同时处理多个下载链接,使得下载过程更加流畅,减少因单一线程...
在C# Winform应用中实现多线程下载是一项常见的任务,尤其在处理大文件或需要提高下载速度的情况下。本文将详细讲解如何利用C#的多线程技术来创建一个Winform应用程序,实现高效的文件下载功能。 首先,我们需要...
在Delphi编程环境中,利用 Indy (Internet Direct) 的 `TIdHTTP` 组件进行多线程下载是一项常见的任务,尤其适用于处理大文件,以提高下载效率并提供更好的用户体验。`TIdHTTP` 是Indy库中的一个核心组件,用于执行...
本文将深入探讨如何在C#中实现多线程下载文件,涉及的知识点包括线程池、异步编程、进度更新以及错误处理。 首先,我们需要了解线程的基本概念。在C#中,线程是程序执行的独立路径,每个线程都有自己的调用堆栈,...
《多线程下载器——易语言项目实例解析》 在当今高速发展的互联网世界中,高效的数据传输成为了一项重要的需求。多线程下载器作为提升下载速度的有效工具,被广泛应用于各种场景。本文将深入剖析一个基于易语言实现...