该断点下载可应用于浏览器或者迅雷等下载工具的下载,实现方式有多种多样的,本文只研究了单线程的下载,迅雷等下载工具会自动将下载资源分块并记录每块的起始位置,然后根据系统性能,起多线程下载。
1. 基本原理
从Request Header的Range信息里面获取已经下载的文件大小,然后创建response的outputstream 向客户端(浏览器或者迅雷等下载工具)写,写的时候又利用header里面的“Content-Range”, 让客户端知道从哪个位置开始写;
读取网络资源方面,利用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的输出流程来向客户端提供下载功能,而不是简单的把数据写入到某个具体的文件,核心代码:out = response.getOutputStream();
– 头信息里面”Range” 和 “Conent-Range” 等信息的处理;
– 迅雷等多线程分块下载客户端下载的处理:还是要处理好”Range” 和 “Conent-Range” 等头部信息,迅雷会自动将文件内容分块、记录起始位置。
来自:http://blog.csdn.net/alli0968/article/details/47337349
原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/10369.html