Java 实现的断点下载详解编程语言

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

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

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论