文档章节

Java实现文件断点下载。

奔跑的码农
 奔跑的码农
发布于 2017/06/02 15:15
字数 872
阅读 23
收藏 1

HTTP头字段(英语:HTTP header fields)是指在超文本传输协议(HTTP)的请求和响应消息中的消息头部分。它们定义了一个超文本传输协议事务中的操作参数。(维基百科)

 

本篇文章用到的HTTP头字段有:

Range:    仅请求某个实体的一部分。字节偏移以0开始。参考 字节服务 。

Range: bytes=500-999 (500-)(-500)分别表示从文件的第500字节开始下载,到999字节(500之后的字节)(最后500个字节)。

Accept-Ranges:    这个服务器支持哪些种类的部分内容范围。

Accept-Ranges: bytes: 表示服务器支持bytes的的分段方式。

Content-Length:    回应消息体的长度,以 字节 (8位为一字节)为单位.

Content-Length: 348:    文件大小为348字节。

Content-Range:    这条部分消息是属于某条完整消息的哪个部分。

Content-Range: bytes 21010-47021/47022 表示服务器输出的文件是21010字节到47021字节,文件总大小47022字节。

首先需要一个文件随机读取的通用类,代码如下:

/** * 根据文件的起始未知和要读的字节书 * @param downloadFileName 要下载的文件名 * @param start 起始位置 * @param size 要读取的文件大小 * @param outputStream 文件输出的位置 */
	public static void download(String downloadFileName,long start,long size,OutputStream outputStream) {
		try{
			//缓冲字节输出流
			BufferedOutputStream out=new BufferedOutputStream(outputStream);
			//注意FILEDIR为文件的根目录
			RandomAccessFile raf=new RandomAccessFile(FILEDIR+"/"+new String(downloadFileName.getBytes("utf-8")), "r");
			write(start, size, raf, out);
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
	/** * 将文件写入输出流 * @param start 文件起始位置(例如:RandomAccessFile.seek(5)是在第六的字节开始读的) * @param size 文件的大小 * @param raf 随机输入流 * @param out 输出流 */
	private static void write(long start,long size,RandomAccessFile raf,BufferedOutputStream out){
		try {
			byte[] buffer=new byte[1024];
			int byteRead=-1;
			long readLength=0;
			raf.seek(start);
			while(readLength<size-1024){
				byteRead=raf.read(buffer, 0, 1024);
				readLength+=1024;
				out.write(buffer,0,byteRead);
			}
			if (readLength<=size) {
				byteRead=raf.read(buffer, 0, (int)(size-readLength));
				out.write(buffer,0,byteRead);
			}
			out.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			try{
				if (raf!=null) {
					raf.close();
				}
			}catch (IOException ex) {
				
			}
			try {
				if (out!=null) {
					out.close();
				}
			} catch (IOException ex) {
			
			}
		}
	}

 

接下来编写servlet

@Override
	protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
		//要下下载的文件名
		String downloadFileName=request.getParameter("filename");
		response.reset();
		//告诉客户端支持的下载方式
		response.setHeader("Accept-Ranges", "bytes");
		String[] sizes=getLength(request, response);
		File file=new File(FileUtil.FILEDIR+"/"+downloadFileName);
		long start=0L;
		long size=file.length();
		if (sizes==null) {
			//如果为空则Range的值为空,即没有指定下载哪一段文件
		}else if (sizes.length==1) {
			start=Long.parseLong(sizes[0])-1L;
			size=size-Long.parseLong(sizes[0]+1L);
		    String contentRange = new StringBuffer(request.getHeader("Range")).append("/").append(size).toString(); 
            contentRange = contentRange.replaceAll("=", " ");
            response.setHeader("Content-Range", contentRange);
            
		}else if (sizes.length==2) {
			//因为RandomAccessFile.seek()是从start的下一个字节开始读的,所以需要-1
			start = Long.parseLong(sizes[0]) - 1L;
            String contentRange = new StringBuffer(request.getHeader("Range")).append("/").append(size).toString();; 
            contentRange = contentRange.replaceAll("=", " ");
            response.setHeader("Content-Range", contentRange); 
            size = Long.parseLong(sizes[1]) - start + 1L;
		}else {
			return;
		}
		  //为了方便测试,下载的文件名指定为a.jpg
		  response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", "a.jpg"));
		  //告诉客户端文件的大小
	      response.addHeader("Content-Length", String.valueOf(size));
	      FileUtil.download(downloadFileName, start, size, response.getOutputStream());
	}
	 private String[] getLength(HttpServletRequest request, HttpServletResponse response){
	     String[] sizes = null;
	     String range = request.getHeader("Range");
	     System.out.println("客户端请求:	"+range);
	     if (range != null && !"".equals(range.trim())) {
	         response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT); 
	         range = range.replaceAll("bytes=", "").trim();
	         sizes = range.split("-");             
	     }
	     return sizes;
	 }

 

使用迅雷精简版可以实现断点下载:下载时输出Range的数据

客户端请求: bytes=4103856-7108546
客户端请求: bytes=1850337-2601509
客户端请求: bytes=5606201-7108546
客户端请求: bytes=2601510-4103855

可以看出迅雷不是多个线程同时下载的,自动分块下载。(个人理解).

© 著作权归作者所有

共有 人打赏支持
奔跑的码农
粉丝 11
博文 25
码字总数 28867
作品 0
海淀
程序员
libcurl在android下的移植、编译与测试以及java接口的封装

curl是利用URL语法在命令行方式下工作的文件传输工具 它支持很多协议:FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 以及 LDAP。 curl同样支持HTTPS认证,HTTP POST方法, HTTP PUT...

zhoulc
2013/03/05
0
2
java远程调试与JVM调优工具

Java远程调试方法: 1、被调试程序当作调试服务器(本地主动连接远程服务器,需要用如下命令让远程服务器jvm开启调试模式)。 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8765 ...

chape
2013/12/24
0
0
eclipse远程debug,java项目

首先java项目要支付可远程调试得到启动时加入参数 启动远程调试 启用JMX,远程性能观察 eclipse配置 其中:host为远程java程序提供远程调试的IP,post为远程java程序提供远程调试的端口 使用 ...

有事没事
2014/12/05
0
0
探秘IntelliJ IDEA 13测试版新功能——调试器显示本地变量

IntelliJ IDEA在业界被公认为最好的Java开发平台之一,JetBrains公司将在12月正式发布IntelliJ IDEA 13版本。 现在,小编将和大家一起探秘密IntelliJ IDEA 13测试版本新功能——调试器显示本...

kouxunli1
2013/10/23
0
2
用Java实现HTTP断点续传(2)

(二)Java实现断点续传的关键几点 (1)用什么方法实现提交RANGE: bytes=2000070-。 当然用最原始的Socket是肯定能完成的,不过那样太费事了,其实Java的net包中提供了这种功能。代码如下: UR...

millzhang
2011/05/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

go语言学习总结

一、描述 go语言是直接将源码编译成二进制机器码的语言;它支持面向对象、也支持函数式编程;支持并发很容易; 二、基本语法学习路径 https://studygolang.com/subject/2...

盼望明天
12分钟前
1
0
JSP 九大内置对象及其作用域

JSP 中一共预先定义了 9 个这样的对象,分别为:request、response、session、application、out、pagecontext、config、page、exception ,下面就简单介绍下。 1、request 对象 request 对象...

几个栗子
23分钟前
0
0
Java中的坑之方括号

Java中的坑之方括号 这一段时间,在做项目的时候,发现了一个坑,这个坑说大不大,说小不小,不知道的足够喝一壶,知道的就可以轻松解决。 问题描述 在做数据统计的时候,遇见了如下形式的数...

星汉
34分钟前
1
0
[雪峰磁针石博客]python机器学习、web开发等书籍汇总

Building Machine Learning Systems with Python Third Edition - 2018.pdf 下载地址 Get more from your data by creating practical machine learning systems with Python Key Features ......

python测试开发人工智能安全
45分钟前
1
0
文件的压缩与解压(linux)

Linux下*.tar.gz文件解压缩命令 1.压缩命令:   命令格式:tar -zcvf 压缩后文件名.tar.gz 被压缩文件名 可先切换到当前目录下。压缩文件名和被压缩文件名都可加入路径。 2.解压缩命令: ...

qimh
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部