java 断点下载

时间:2025-08-30 10:30:01来源:互联网

下面小编就为大家分享一篇java 断点下载,具有很好的参考价值,希望对大家有所帮助。

功能说明

已经下载一部分,网络断开等原因导致没有下载完,问题消失后还可以继续下载,不用从头开始下载

实现

说明,前端要传过来文件总大小和已经下载的大小,不同的文件服务器的断点下载的api不同;返回给前端的时候根据是否是断点下载返回不同的Content-Length和Content-Range即可。

@ApiOperation(value = "断点下载",notes = "断点下载需要传请求头Size,Range")
@GetMapping(path ="/keepOnDownload", name = "断点下载")
public void keepOnDownload(String fileName, @RequestHeader HttpHeaders headers , HttpServletResponse response) {
     fileService.keepOnDownload(fileName,headers,response);
}
public void keepOnDownload(String fileName, HttpHeaders headers, HttpServletResponse response) {
        log.info(JSONUtil.toJsonStr(headers));
        List<String> fileSize = headers.get("Size");//文件总大小
//        List<String> offset = headers.get("Offset");//已下载大小
        List<String> ranges = headers.get("Range");//已下载大小
        String offsetStr =null;
        String rangeStr =null;
        if(CollectionUtil.isEmpty(fileSize)){
            throw new GlobalDefaultException(ResponseCode.METHOD_ARGUMENT_NOT_VALID.getCode(),"请求头参数异常");
        }
        String contentLength = fileSize.get(0);
        String end=null;
        if(CollectionUtil.isNotEmpty(ranges)){
            rangeStr=ranges.get(0);
            offsetStr=rangeStr;
            String[] split = offsetStr.split("-");
            if(split.length>1){
                end =split[1];
            }
            offsetStr = rangeStr.replace("bytes=", "");
            offsetStr = offsetStr.substring(0, offsetStr.indexOf('-'));
        }

        log.info("Size:{},Offset:{}",contentLength,offsetStr);
        Long range =StrUtil.isBlank(offsetStr)?null:Long.parseLong(offsetStr);
        Long rangeEnd=StrUtil.isBlank(end)?Long.parseLong(contentLength)-1:Long.parseLong(end);
        InputStream fis=null;
        if(range==null || range <= 0){
            if("bytes=0-0".equals(rangeStr) ){
                response.setHeader("Accept-Ranges","bytes");
                response.setHeader("Content-Length", "0" );
                response.setHeader("Content-Range", "bytes 0-0/"+contentLength );
                fis=getFileStreamKeepOn(fileName,0L,0L);
            }else{//0-获取所有数据
                response.setHeader("Accept-Ranges","bytes");
                response.setHeader("Content-Length", rangeEnd+1+"");
                response.setHeader("Content-Range", "bytes 0-"+(rangeEnd)+"/"+contentLength );
                fis=getFileStreamKeepOn(fileName,range,rangeEnd);
            }
        }else{
            response.setHeader("Accept-Ranges","bytes");
            response.setHeader("Content-Length", rangeEnd-Long.parseLong(offsetStr)+1+"" );
            response.setHeader("Content-Range", "bytes "+range+"-"+rangeEnd+"/"+contentLength );
            fis=getFileStreamKeepOn(fileName,range,rangeEnd);
        }
        try {
            // 文件名可以任意指定
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName,"UTF-8"));
            response.setContentType(OssFileUtil.getContentType(fileName));
            if(fis!=null){
                int size = FileUtil.copy(fis, response.getOutputStream());
                log.info("下载大小:{}",size);
            }else{
                log.info("下载大小:{}",0);
            }

        } catch (Exception e) {
            log.error("下载出错了:{}",e.getMessage());
        }
    }
public InputStream getFileStreamKeepOn(String fileName, Long begin,Long end) {
        InputStream input = null;
        if("minio".equals(uploadType)){
            input = minioFileService.getInputStream(fileName,begin,end);
        }else if("oss".equals(uploadType)){
            input = ossFileService.getInputStream(fileName,begin,end);
        }else if("s3oss".equals(uploadType)){
            input = s3OssFileUtil.getInputStream(fileName,begin,end);
        }
        return input;
    }

minioFileService

public InputStream getInputStream(String fileName, Long begin,Long end) {
        return minioFileUtil.getInputStream(fileName,begin,end-begin+1);
    }
public InputStream getInputStream(String fileName,Long offset,Long length) {
        String bucketName ="bucketName";
        fileName = "file/"+fileName;
        try {
            return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).offset(offset).length(length).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

 ossFileServie

public InputStream getInputStream(String fileName,Long begin,Long end){
        String bucketName = "bucketName";
        String key = "file/" + fileName;
        GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucketName, key );
        Date expiration = new Date(new Date().getTime() + 3600 * 1000);
        // 设置过期时间。
        req.setExpiration(expiration);
        URL signedUrl = ossClient.generatePresignedUrl(req);
        Map<String,String> customHeaders = new HashMap<>();
        // + end
        String strEnd=end==null?"":end+"";
        customHeaders.put("Range","bytes="+begin+"-"+strEnd);
        OSSObject object = ossClient.getObject(signedUrl, customHeaders);
        return object.getObjectContent();
    }

s3ossService

 public InputStream getInputStream(String fileName,Long begin,Long end){
        String bucketName = s3OSSConfig.getBucketName();
        String key = s3OSSConfig.getFolder() + fileName;
        try {
            GetObjectRequest getObjectRequest=new GetObjectRequest(bucketName,key);
            getObjectRequest.withRange(begin,end);
            return s3Client.getObject(getObjectRequest).getObjectContent();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

 

/**
     * Description: 判断OSS服务文件上传时文件的contentType
     * @param fileName 文件名
     * @return String
     */
    public static String getContentType(String fileName) {
        // 文件的后缀名
        String fileExtension = fileName.substring(fileName.lastIndexOf("."));
        if (".bmp".equalsIgnoreCase(fileExtension)) {
            return "image/bmp";
        }else if (".gif".equalsIgnoreCase(fileExtension)) {
            return "image/gif";
        }else if (".jpeg".equalsIgnoreCase(fileExtension) || ".jpg".equalsIgnoreCase(fileExtension)
                || ".png".equalsIgnoreCase(fileExtension)) {
            return "image/jpg";
        }
//        else if (".png".equalsIgnoreCase(fileExtension)) {
//            return "image/png";
//        }
        else if (".html".equalsIgnoreCase(fileExtension)) {
            return "text/html";
        }else if (".txt".equalsIgnoreCase(fileExtension)) {
            return "text/plain";
        }else if (".vsd".equalsIgnoreCase(fileExtension)) {
            return "application/vnd.visio";
        }else if (".ppt".equalsIgnoreCase(fileExtension) || ".pptx".equalsIgnoreCase(fileExtension)) {
            return "application/vnd.ms-powerpoint";
        }else if (".doc".equalsIgnoreCase(fileExtension) || ".docx".equalsIgnoreCase(fileExtension)) {
            return "application/msword";
        }else if (".xml".equalsIgnoreCase(fileExtension)) {
            return "text/xml";
        }else if (".pdf".equalsIgnoreCase(fileExtension)) {
            return "application/pdf";
        }else if(".apk".equalsIgnoreCase(fileExtension)){
            return "application/vnd.android.package-archive";
        }else if(".xlsx".equalsIgnoreCase(fileExtension)){
            return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
        }
        // 默认返回类型
        return "image/jpg";
    }
 /**
     * byte 转file
     */
    public static File byte2File(byte[] buf, File file) throws IOException {
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        try{
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(buf);
        }finally{
            if (bos != null)
                bos.close();
            if (fos != null)
                fos.close();
        }
        return file;
    }

    public static int copy(InputStream in, OutputStream out) throws Exception {
        Assert.notNull(in, "No InputStream specified");
        Assert.notNull(out, "No OutputStream specified");
        int byteCount = 0;
        try {
            //大多数文件系统使用4096或8192的块大小。
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {//将输入流数据读取到byte[]中
                out.write(buffer, 0, bytesRead);//将byte[]中数据写入到输出流中
                byteCount += bytesRead;
            }
            out.flush();//当输出流是BufferedOutputStream时才有效,用于主动触发写入磁盘,而不必等待byte[]满了以后
            return byteCount;
        }catch (Exception e){
            log.error("下载过程中出错,已下载大小:{}",byteCount,e);
            throw e;
        }

        finally {
            try {
                in.close();
            }catch (Exception e){
                log.error("关闭输入流出错了",e.getMessage());
//                e.printStackTrace();
            }
            try{
                out.close();
            }catch (Exception e){
                log.error("关闭输出流出错了",e.getMessage());
//                e.printStackTrace();
            }
        }
    }

 

本站部分内容转载自互联网,如果有网站内容侵犯了您的权益,可直接联系我们删除,感谢支持!