热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java实现断点续传服务

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 brWrapper=new LambdaQueryWrapper<>();
brWrapper.eq(BreakpointResume::getCodKey,key);
List 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 {
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 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);
}
}


推荐阅读
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Redis底层数据结构之压缩列表的介绍及实现原理
    本文介绍了Redis底层数据结构之压缩列表的概念、实现原理以及使用场景。压缩列表是Redis为了节约内存而开发的一种顺序数据结构,由特殊编码的连续内存块组成。文章详细解释了压缩列表的构成和各个属性的含义,以及如何通过指针来计算表尾节点的地址。压缩列表适用于列表键和哈希键中只包含少量小整数值和短字符串的情况。通过使用压缩列表,可以有效减少内存占用,提升Redis的性能。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • asp中如何嵌入python的简单介绍
    本文目录一览:1、如何在IIS中执行Python脚本 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 本文介绍了使用cacti监控mssql 2005运行资源情况的操作步骤,包括安装必要的工具和驱动,测试mssql的连接,配置监控脚本等。通过php连接mssql来获取SQL 2005性能计算器的值,实现对mssql的监控。详细的操作步骤和代码请参考附件。 ... [详细]
  • 本文介绍了如何使用JSONObiect和Gson相关方法实现json数据与kotlin对象的相互转换。首先解释了JSON的概念和数据格式,然后详细介绍了相关API,包括JSONObject和Gson的使用方法。接着讲解了如何将json格式的字符串转换为kotlin对象或List,以及如何将kotlin对象转换为json字符串。最后提到了使用Map封装json对象的特殊情况。文章还对JSON和XML进行了比较,指出了JSON的优势和缺点。 ... [详细]
  • 本文介绍了关于Java异常的八大常见问题,包括异常管理的最佳做法、在try块中定义的变量不能用于catch或finally的原因以及为什么Double.parseDouble(null)和Integer.parseInt(null)会抛出不同的异常。同时指出这些问题是由于不同的开发人员开发所导致的,不值得过多思考。 ... [详细]
author-avatar
品格优良2003_645
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有