博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
支持网络文件断点续传的实现
阅读量:6153 次
发布时间:2019-06-21

本文共 7472 字,大约阅读时间需要 24 分钟。

 

断点续传的原理:一个文件下载了一部分后,由于服务器或客户端原因,当前下载进度中断,用户可继续重新建立网络连接继续下载未下完的部分

断点三个最主要的属性:   Code:连接返回响应状态,状态码206支持断点续传

                       
                        Range属性:下载区域,它接收是一个区间范围,比如:Range:bytes=0-10000
                        例如:下载8.zip文件要求从10086字节开始传,前面的字节不用传,那么RANGE: bytes=10086-

                        Content-Range:bytes start- (fileSize-1)/fileSize;注释:fileSize文件总大小,start续传区间,从哪个位置开始续传的位置

首次请求支持断点续传服务器:

请求:

GET /down.zip HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-
excel, application/msword, application/vnd.ms-powerpoint, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
Connection: Keep-Alive

响应:

200
Content-Length=106786028
Accept-Ranges=bytes
Date=Mon, 30 Apr 2013 12:56:11 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2013 12:56:11 GMT

第二次请求支持断点续传服务器:

请求:

GET /down.zip HTTP/1.0
User-Agent: NetFox
RANGE: bytes=2000070-
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

响应:

206
Content-Length=106786028
Content-Range=bytes 2000070-106786027/106786028
Date=Mon, 30 Apr 2013 12:55:20 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2013 12:55:20 GMT

示例图:

 

代码示例:

支持断点续传后台服务:

import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.PrintWriter;import java.io.RandomAccessFile;import javax.servlet.ServletException;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@SuppressWarnings("serial")public class RetryBokenDownloadsServlet extends HttpServlet {		public RetryBokenDownloadsServlet() {		super();	}	public void init() throws ServletException {		super.init();	}		public void doGet(HttpServletRequest request, HttpServletResponse response)			throws ServletException, IOException {				    //定义一个任意访问文件位置的RandomAccessFiled对象		    RandomAccessFile raFile = null;		    //获取响应输出流对象		    ServletOutputStream sos = null;			 try {			//下载网络杂志的相对路径			String downloadFile = request.getParameter("downloadFile");			//下载文件名			String fileName = request.getParameter("fileName");			//获取绝对路径下网络杂志文件大小			int fileSize = getFileByte(FileUtil.SITE_PATH + downloadFile);			//设置下载文件大小			response.addHeader("content-file-length", "" + (fileSize));			//定义一个任意访问文件位置的RandomAccessFiled对象			raFile = new RandomAccessFile(FileUtil.SITE_PATH + downloadFile, "r");	        //设置获取客户端请求RANGE,获取下载文件的字节开始位置和结束位置			String range = request.getHeader("RANGE");			int status =200; //返回的状态码,默认200,首次下载			//如果range下载区域为空,则首次下载,			if(range ==null){				range="bytes=0-";			}else{				//非首次下载通过下载区域下载使用206状态码支持断点续传				status =206;			}			int start = 0, end = 0;			if (null != range && range.startsWith("bytes=")) {				String[] values = range.split("=")[1].split("-");				start = Integer.parseInt(values[0]);				//如果服务器端没有设置end结尾,默认取下载全部				if(values.length==1){					end = fileSize;				}else{					end = Integer.parseInt(values[1]);				}							}			//此次数据响应大小			int responseSize = 0;			if (end != 0 && end > start) {				responseSize = end - start + 1;				//返回当前连接下载的数据大小,也就是此次数据传输大小				response.addHeader("content-length", "" + (responseSize));			} else {				responseSize = Integer.MAX_VALUE;			}                        //设置缓存为要下载的文件大小一半,减少太频繁并发连接数超过了其承载量而出现 ClientAbortException:  java.io.IOException异常,在windows7本地调试不会,在window2003和linux会出现,待排查                        //超过20M,缓存设置默认20M左右,如视频,防止内存溢出                        int maxSize = 1024*1024*20;                        byte[] buffer = null;                        if (fileSize > maxSize){                                buffer = new byte[1024*1024*20];                        }else{
                buffer =  new byte[fileSize/2];             } //设置响应状态码 response.setStatus(status); if(status == 206){ //设置断点续传的Content-Range传输字节和总字节 response.addHeader("Content-Range","bytes "+start+"-"+(fileSize-1)+"/"+fileSize); } //设置响应客户端内容类型 response.setContentType("application/x-download"); //设置响应客户端头 response.addHeader("Content-Disposition", "attachment;filename="+ fileName); //response.addHeader("Content-Range","bytes ""1000-5000/5001" ); //获取响应输出流对象 sos = response.getOutputStream(); //当前需要下载文件的大小 int needSize = responseSize; //将下载网络杂志记录指针定位到start位置 raFile.seek(start); while (needSize > 0) { int len = raFile.read(buffer); if (needSize < buffer.length) { sos.write(buffer, 0, needSize); } else { sos.write(buffer, 0, len); //如果读取文件大小小于缓冲字节大小,表示已写入完,直接跳出 if (len < buffer.length) { break; } } //不断更新当前可下载文件大小 needSize -= buffer.length; } } catch (Exception e) { e.printStackTrace(); }finally{ //关闭流 if(raFile != null){ raFile.close(); } if(sos != null){ sos.close(); } } } public void doPost(HttpServletRequest reuqest, HttpServletResponse response) throws ServletException, IOException { doGet(reuqest,response); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println(""); out.println(""); out.println(""); out.flush(); out.close(); } /** * 获取文件字节总大小 * @param filePath 文件绝对路径(硬盘路径) * @return * @throws IOException */ private static int getFileByte(String filePath) throws IOException { File file = new File(filePath); FileInputStream fs = null; int fileSize = 0; try { fs = new FileInputStream(file); fileSize = fs.available(); } catch (Exception e) { e.printStackTrace(); }finally{ if(fs!=null){ fs.close(); } } return fileSize; } public void destroy() { super.destroy(); } }

 

测试类:

/**    * 断点续传下载     * @param start 开始位置     * @param end 结束位置     * @throws MalformedURLException     * @throws FileNotFoundException    */    public static void retryBokenDownloads(int start, int end){                    String testUrl= "xxxxxxx.zip";        try {            File file = new File("E:/temp/8.zip");           if(file.exists()){              System.out.println("获取已下载的文件大小="+file.length());           }else{             System.out.println("获取已下载的文件大小0");           }            RandomAccessFile raFile = new RandomAccessFile("E:/temp/8.zip", "rw");              URL url = new URL(endpoint);              HttpURLConnection conn = (HttpURLConnection) url.openConnection();               conn.setRequestProperty("Content-Type","text/html; charset=UTF-8");               conn.setRequestProperty("RANGE","bytes="+start+"-"+end);               conn.connect();            System.out.println(conn.getResponseCode());    //获取服务端的返回的响应码,200-返回连接成功              System.out.println(conn.getContentLength());   //获取服务端的内容长度              System.out.println(conn.getContentType());    //获取服务端的内容类型              System.out.println(conn.getHeaderField("content-file-length"));  //获取服务端的文件大小              System.out.println(conn.getHeaderField("Content-Range"));        //获取服务端的文件大小             //System.out.println(conn.getHeaderField("content-length"));      //连接服务端下载的大小              InputStream ins = (InputStream)conn.getContent();              raFile.seek(start);                            byte[] buffer = new byte[4096];              int len = -1;              while((len = ins.read(buffer))!=-1){                  raFile.write(buffer,0,len);              }              raFile.close();              conn.disconnect();          } catch (Exception e) {              e.printStackTrace();          }      }}

 

转载地址:http://vsffa.baihongyu.com/

你可能感兴趣的文章
restful
查看>>
单线程爬虫实现
查看>>
锁与线程
查看>>
bzoj 3223: Tyvj 1729 文艺平衡树
查看>>
MySQL高级 之 order by、group by 优化
查看>>
JavaScript学习笔记(三)
查看>>
PyQt4学习笔记2:事件和信号
查看>>
windows系统实现mysql数据库数据库主从复制
查看>>
elasticsearch5.0.1集群排错的几个思路总结
查看>>
Linux 平台设备驱动模型
查看>>
前端学习之jquery
查看>>
RedHat6.2搭建FTP服务器
查看>>
DataTable学习笔记
查看>>
this指向问题
查看>>
原生查找DOM的方法
查看>>
Global variables vs. Host variables vs. Parameter markers
查看>>
百度电影推荐系统比赛 小结 ——记我的初步推荐算法实践
查看>>
HDU2033 人见人爱A+B
查看>>
天龙八部中的诗词
查看>>
jQuery CSS 添加/删除类名
查看>>