该断点下载可应用于浏览器或者迅雷等下载工具的下载,实现方式有多种多样的,本文仅仅研究了单线程的下载。迅雷等下载工具会自己主动将下载资源分块并记录每块的起始位置,然后依据系统性能。起多线程下载。

1. 基本原理

从Request Header的Range信息里面获取已经下载的文件大小,然后创建response的outputstream 向client(浏览器或者迅雷等下载工具)写,写的时候又利用header里面的“Content-Range”, 让client知道从哪个位置開始写;

读取网络资源方面,利用HttpClient模拟request请求,发起post或者get请求,仅仅是这个请求跟一般请求有点不一样:须要带上Range信息。告诉程序该从哪个位置開始读数据。

2. 须要使用的Java 组件

  • HttpServletRequest / Response
  • HttpClient
  • ServletOutputStream
  • BufferedInputStream

3. 代码实现

/**
* @desc 断点下载工具方法
* @param request
* @param response
* @param fileLength
* @param contentType
* @param fileName
* @param fileId
*/
public static void resumeDownload(HttpServletRequest request,
HttpServletResponse response, Long fileLength, String contentType,
String fileName, String fileId) {
ServletOutputStream out = null;
response.reset(); // 记录断点续传的開始点
long pos = 0;
if (null != request.getHeader("Range")) {
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
try {
pos = Long.parseLong(request.getHeader("Range")
.replaceAll("bytes=", "").replaceAll("-.*", ""));
} catch (NumberFormatException e) {
LOGGER.error(e.getMessage(), e);
pos = 0;
}
String contentRange = new StringBuffer("bytes ").append(pos + "")
.append("-").append((fileLength.intValue() - 1) + "")
.append("/").append(fileLength.intValue() + "").toString();
response.setHeader("Content-Range", contentRange);
} response.setHeader("Accept-Ranges", "bytes");
response.setHeader("Content-Length",
String.valueOf(fileLength.intValue() - pos));
response.setCharacterEncoding("UTF-8");
response.setContentType(contentType);
response.setHeader("Content-disposition", "attachment;filename=\""
+ fileName + "\"");
try {
out = response.getOutputStream();
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
} // 断点下载
CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost(SysConf.getString("fezo.download.url")); List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair(SysConf.getString("fezo.download.param"), fileId)); HttpResponse httpResponse = null;
BufferedInputStream input = null;
try {
httpPost.setEntity(new UrlEncodedFormEntity(nvps)); httpPost.setHeader("Range", "bytes=" + pos + "-");
httpResponse = httpClient.execute(httpPost); input = new BufferedInputStream(httpResponse.getEntity().getContent()); byte[] buffer = new byte[CommonConstants.BUFFER_SIZE];
int len = -1;
while ((len = input.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.flush();
out.close();
input.close();
} catch (UnsupportedEncodingException e) {
LOGGER.error(e.getMessage(), e);
} catch (ClientProtocolException e) {
LOGGER.error(e.getMessage(), e);
} catch (IOException e) {
// 能够忽略这个异常。有可能是用户暂停下载,或者迅雷等下载工具分块下载
} finally {
try {
if (httpClient != null) httpClient.close();
} catch(IOException e) {
LOGGER.error(e.getMessage(), e);
}
}
}

>>>点击这里下载代码

4. 重点与难点

- 获取response的输出流程来向client提供下载功能,而不是简单的把数据写入到某个详细的文件,核心代码:out = response.getOutputStream();

- 头信息里面"Range" 和 "Conent-Range" 等信息的处理;

- 迅雷等多线程分块下载client下载的处理:还是要处理好"Range" 和 "Conent-Range" 等头部信息,迅雷会自己主动将文件内容分块、记录起始位置。

最新文章

  1. c++学习笔记——智能指针
  2. JavaPersistenceWithHibernate第二版笔记-第五章-Mapping value types-004嵌套组件的注解AttributeOverrides
  3. Quartz任务调度快速入门(转)
  4. Caused by: java.lang.ClassNotFoundException: javax.transaction.TransactionManager
  5. C语言第四次博客作业
  6. 从PRISM开始学WPF(九)交互(完结)
  7. android加载大图,防止oom
  8. cetos6.8配置svn服务器
  9. Redis操作hash
  10. js中bind的用法,及与call和apply的区别
  11. 腾讯云下的CentOS7 配置 FTP 服务器
  12. 蛙蛙推荐: TensorFlow Hello World 之平面拟合
  13. Hdoj 2501.Tiling_easy version 题解
  14. C# 读写西门子PLC数据,包含S7协议和Fetch/Write协议,s7支持200smart,300PLC,1200PLC,1500PLC
  15. NF5280M4 安装 Win2016 的方法
  16. 修复vs2012出现 “无法找到包源”的错误
  17. Oracle APEX 5.1 with Ords 17 in Tomcat 9&ndash;Error tips: 请求无法映射到任何数据库。请确保请求 URL 正确, 并且已正确配置 URL 到数据库的映射
  18. 【Java集合源码剖析】Hashtable源码剖析
  19. Android 编译参数 LOCAL_MODULE_TAGS
  20. Linux NAPI处理流程分析

热门文章

  1. springMVC接收get请求传递多个参数
  2. HDU_1561_The more, The Better_树型dp
  3. IOS7 状态栏和 Navigation Bar重叠的问题解决
  4. python3 操作excel表
  5. php第二十八节课
  6. 关于Extjs的窗口拖拽,改变大小,背景淡化问题
  7. C++ Primer(第4版)-学习笔记-第5部分:高级主题
  8. Diango REST framework 视图继承图
  9. jdk版本特性
  10. sql杂记:一些坑和数据库恢复