zoukankan      html  css  js  c++  java
  • Java 实现的断点下载

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

    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" 等头部信息,迅雷会自己主动将文件内容分块、记录起始位置。
  • 相关阅读:
    [ jquery 选择器 :nth-last-child ] 选取属于其父元素的不限类型的第 n 个子元素的所有元素,从最后一个子元素开始计数
    [ jquery 选择器 :nth-child ] 选取匹配其父元素下的第N个子或奇偶元素
    [ jquery 选择器 :first :first-child ] 实例分析: :first :first-child 之间在元素选取方面的区别
    [ jquery 位置选择器 :first-child :last-child ] 强化说明:选取属于其父元素和所有兄弟元素中子元素集合中处于第一个(最后一个)位置上符合条件的元素
    [ jquery 位置选择器 :first :last ] 强化说明:获取匹配的第一个(最后第一个)元素
    [ jquery 过滤器 is(expr | jqObj | ele | function) ] 此方法用于在选择器的基础之上根据选择器 DOM元素或 jQuery 对象来检测匹配元素集合
    PAT 甲级 1009 Product of Polynomials
    PAT 甲级 1003 Emergency DFS
    PAT 甲级 1002 A+B for Polynomials
    PAT 甲级 1001 A+B Format
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/6858388.html
Copyright © 2011-2022 走看看