ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

java 实现断点续传服务

2021-07-28 22:02:08  阅读:246  来源: 互联网

标签:断点续传 服务 String stream file import java CommonResult response


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);
    }
}

标签:断点续传,服务,String,stream,file,import,java,CommonResult,response
来源: https://blog.csdn.net/qq_50990903/article/details/119191020

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有