java 实现断点续传服务
2021/7/28 22:06:21
本文主要是介绍java 实现断点续传服务,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
java 实现断点续传服务
一:什么是断点续传
客户端软件断点续传指的是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载
(将文件分片以及后续合并是一个不小的工作量,由于项目时间有限,我并没有做分片,只是实现了可断点下载)
二:实现原理
2.1 实现思路
需要前端和后端的配合,前端在请求头中 标明 下载开始的位置,后端重标记位置开始向前端输出文件剩余部分。
在简单模式下,前端不需要知道文件大小,也不许要知道文件是否已经下载完毕。当文件可以正常打开时即文件下载完毕。(若想知道文件是否下载完毕,可写个接口比较Range 值与文件大小)
一般服务请求头
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 2001 12:56:11 GMT ETag=W/"02ca57e173c11:95b" Content-Type=application/octet-stream Server=Microsoft-IIS/5.0 Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT
如果要服务器支持断点续传功能的话,需要在请求头中表明文件开始下载的位置
请求头
GET /down.zip HTTP/1.0 User-Agent: NetFox RANGE: bytes=2000070- #表示文件从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 2001 12:55:20 GMT ETag=W/"02ca57e173c11:95b" Content-Type=application/octet-stream Server=Microsoft-IIS/5.0 Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT
三:java代码实现
3.1 BreakPoinService类
import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletResponse; import java.io.*; @Service public class BreakPoinService { //断点续传 public void downLoadByBreakpoint(File file, long start, long end, HttpServletResponse response){ OutputStream stream = null; RandomAccessFile fif = null; try { if (end <= 0) { end = file.length() - 1; } stream = response.getOutputStream(); response.reset(); response.setStatus(206); response.setContentType("application/octet-stream"); response.setHeader("Content-disposition", "attachment; filename=" + file.getName()); response.setHeader("Content-Length", String.valueOf(end - start + 1)); response.setHeader("file-size", String.valueOf(file.length())); response.setHeader("Accept-Ranges", "bytes"); response.setHeader("Content-Range", String.format("bytes %s-%s/%s", start, end, file.length())); fif = new RandomAccessFile(file, "r"); fif.seek(start); long index = start; int d; byte[] buf = new byte[10240]; while (index <= end && (d = fif.read(buf)) != -1) { if (index + d > end) { d = (int)(end - index + 1); } index += d; stream.write(buf, 0, d); } stream.flush(); } catch (Exception e) { try { if (stream != null) stream.close(); if (fif != null) fif.close(); } catch (Exception e11) { } } } //全量下载 public void downLoadAll(File file, HttpServletResponse response){ OutputStream stream = null; BufferedInputStream fif = null; try { stream = response.getOutputStream(); response.reset(); response.setContentType("application/octet-stream"); response.setHeader("Content-disposition", "attachment; filename=" + file.getName()); response.setHeader("Content-Length", String.valueOf(file.length())); fif = new BufferedInputStream(new FileInputStream(file)); int d; byte[] buf = new byte[10240]; while ((d = fif.read(buf)) != -1) { stream.write(buf, 0, d); } stream.flush(); } catch (Exception e) { try { if (stream != null) stream.close(); if (fif != null) fif.close(); } catch (Exception e11) { } } } }
3.2 断点续传控制类
import cn.ztuo.api.cos.QCloudStorageService; import cn.ztuo.api.service.IBreakpointResumeService; import cn.ztuo.api.service.impl.BreakPoinService; import cn.ztuo.commons.annotation.PassToken; import cn.ztuo.commons.response.CommonResult; import cn.ztuo.mbg.entity.BreakpointResume; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.util.Date; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 断点续传控制类 */ @RestController @RequestMapping("/breakpoint") public class BreakPointController { @Autowired private IBreakpointResumeService breakpointResumeService; @Autowired private BreakPoinService breakPoinService; @Autowired private QCloudStorageService storageService; @PassToken @GetMapping(value = "resource") public CommonResult download(HttpServletRequest request, HttpServletResponse response, @RequestParam("key") String key) { LambdaQueryWrapper<BreakpointResume> brWrapper=new LambdaQueryWrapper<>(); brWrapper.eq(BreakpointResume::getCodKey,key); List<BreakpointResume> list = breakpointResumeService.list(brWrapper); String str=null; //如果本地存在取本地文件 if(list.size()>0){ BreakpointResume breakpointResume = list.get(0); str=breakpointResume.getFilePath(); }else{//本地不存在 try{ String download = storageService.download(key); BreakpointResume breakpointResume=new BreakpointResume(); breakpointResume.setCodKey(key); breakpointResume.setFilePath(download); breakpointResume.setCreateTime(new Date()); breakpointResume.setUpdateTime(new Date()); boolean save = breakpointResumeService.save(breakpointResume); if(save){ str=download; }else{ return CommonResult.error(); } }catch (Exception e){ return CommonResult.error(); } } if(str==null){ return CommonResult.error(); } File file=new File(str); if (file.exists()) { String range = request.getHeader("Range"); if (range != null && (range = range.trim()).length() > 0) { Pattern rangePattern = Pattern.compile("^bytes=([0-9]+)-([0-9]+)?$"); Matcher matcher = rangePattern.matcher(range); if (matcher.find()) { Integer start = Integer.valueOf(matcher.group(1)); Integer end = 0; String endStr = matcher.group(2); if (endStr != null && (endStr = endStr.trim()).length() > 0) end = Integer.valueOf(endStr); breakPoinService.downLoadByBreakpoint(file, start, end, response); return null; } } breakPoinService.downLoadAll(file, response); return null; } return CommonResult.error(); } }
3.3 自定义全局响应类
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class CommonResult<T> { private String code; private String msg; private T data; public CommonResult(String code,String msg){ this.code=code; this.msg=msg; } public static CommonResult success(){ return create("200","成功"); } public static <T> CommonResult success(T data){ CommonResult result = create("200", "成功"); result.setData(data); return result; } public static CommonResult error(){ return create("500","服务器开小差了"); } public static CommonResult create(String code,String msg){ return new CommonResult(code,msg); } }
这篇关于java 实现断点续传服务的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-26大厂数据结构与算法教程:入门级详解
- 2024-12-26大厂算法与数据结构教程:新手入门指南
- 2024-12-26Python编程入门指南
- 2024-12-26数据结构高级教程:新手入门及初级提升指南
- 2024-12-26并查集入门教程:从零开始学会并查集
- 2024-12-26大厂数据结构与算法入门指南
- 2024-12-26大厂算法与数据结构入门教程
- 2024-12-26二叉树入门教程:轻松掌握基础概念与操作
- 2024-12-26初学者指南:轻松掌握链表
- 2024-12-26平衡树入门教程:轻松理解与应用