Springboot Minio最新版大文件下载

上节我们说了Minio怎么大文件上传,我们是进行了分段上传,然后合并处理,感兴趣的可以去这篇文章,

Springboot Minio最新版大文件上传-CSDN博客

超详细的SpringBoot Minio大文件下载业务,我们唠唠代码设计理念

那么今天的主题就是大文件下载,再大文件就需要分段下载,也就需要前端给下载的范围,就是下面的range的参数,我们为了好测试将此字段放入了参数了,实际你可以放入header头部。

下载的Controller类:

@Slf4j
@RestController
public class DownloadController {


    @Resource
    private IDownloadProcess downloadProcess;

    // http://localhost:8082/download?filename=a9500aa2091875f3d02a9b84ae1ab712.mp4&range=bytes=0-52428800
    // 分段下载的化,支持断点下载,暂停下载,断网恢复下载等。
    // 我测试就采取这种方式RequestParam,大家真实场景可以放到header里 @RequestHeader(name = "Range", required = false) String range,
    @GetMapping("/download")
    public ResponseEntity downloadFile(@RequestParam String filename,
                                       @RequestParam(required = false) String range,
                                       HttpServletRequest request, HttpServletResponse response) {
        try {
            return downloadProcess.downloadFile(filename, range, request, response);
        } catch (Exception e) {
            log.error("下载异常|参数:{},{}|{}", filename, range, e);
            return new ResponseEntity<byte[]>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

}

IDownloadProcess:定义下载接口

public interface IDownloadProcess {
    ResponseEntity downloadFile(String filename, String range, HttpServletRequest request, HttpServletResponse response) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException, Exception;
}

DownloadProcessImpl:下载实现类,

我们可以传range参数来处理要下载的kb数范围,当然也可以不传递就是下载全部,

1.首先就是获取桶里文件信息,文件大小什么的都能获取

2.查看是范围下载还是全部下载

3.设置响应下载的类型和请求头

4.获取minio的流文件

5.将流文件遍历读取放入缓冲中

6.然后写入到OutputStream流中,然后刷新就可以啦。

@Slf4j
@Service
public class DownloadProcessImpl implements IDownloadProcess {

    @Resource
    private MinioClient minioClient;

    @Resource
    private MinioConfig minioConfig;

    // 完整文件与分片文件下载
    @Override
    public ResponseEntity downloadFile(String filename, String range, HttpServletRequest request, HttpServletResponse response) throws Exception {
        ResponseEntity<byte[]> responseEntity = null;
        BufferedOutputStream os = null;
        GetObjectResponse stream = null;
        if (StringUtils.isNotBlank(filename)) {
            log.info("要下载的文件:{}", filename);
            //String range = request.getHeader("Range");
            log.info("current request rang:{}", range);
            // 获取桶里文件信息
            StatObjectResponse statObjectResponse = minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(filename)
                            .build());
            //开始下载位置
            long startByte = 0;
            //结束下载位置
            long endByte = statObjectResponse.size() - 1;
            log.info("文件开始位置:{},文件结束位置:{},文件总长度:{}", startByte, endByte, statObjectResponse.size());

            // 有range的话,需要根据前端下载长度进行下载,也就是分段下载
            // 例如:range=bytes=0-52428800
            if (StringUtils.isNotBlank(range) && range.contains("bytes=") && range.contains("-")) {
                range = range.substring(range.lastIndexOf("=") + 1).trim();
                String[] ranges = range.split("-");
                //判断range的类型
                if (ranges.length == 1) {
                    //类型一:bytes=-2343
                    if (range.startsWith("-")) endByte = Long.parseLong(ranges[0]);

                    //类型二:bytes=2343-
                    if (range.endsWith("-")) startByte = Long.parseLong(ranges[0]);

                }
                //类型三:bytes=22-2343
                else if (ranges.length == 2) {
                    startByte = Long.parseLong(ranges[0]);
                    endByte = Long.parseLong(ranges[1]);
                }
            }

            //要下载的长度
            long contentLength = endByte - startByte + 1;
            //文件类型
            String contentType = request.getServletContext().getMimeType(filename);

            //解决下载文件时文件名乱码问题
            byte[] fileNameBytes = filename.getBytes(StandardCharsets.UTF_8);
            filename = new String(fileNameBytes, 0, fileNameBytes.length, StandardCharsets.ISO_8859_1);

            //各种响应头设置---------------------------------------------------------------------------------------------
            //支持断点续传,获取部分字节内容:
            response.setHeader("Accept-Ranges", "bytes");
            //http状态码要为206:表示获取部分内容,SC_PARTIAL_CONTENT,部分浏览器不支持,所以改成SC_OK
            response.setStatus(HttpServletResponse.SC_OK);
            response.setContentType(contentType);
            response.setHeader("Last-Modified", statObjectResponse.lastModified().toString());
            //inline表示浏览器直接使用,attachment表示下载,fileName表示下载的文件名
            response.setHeader("Content-Disposition", "attachment;filename=" + filename);
            response.setHeader("Content-Length", String.valueOf(contentLength));
            //Content-Range,格式为:[要下载的开始位置]-[结束位置]/[文件总大小]
            response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + statObjectResponse.size());
            response.setHeader("ETag", "\"".concat(statObjectResponse.etag()).concat("\""));
            response.setContentType("application/octect-stream;charset=UTF-8");


            try {
                // 获取文件流
                stream = minioClient.getObject(
                        GetObjectArgs.builder()
                                .bucket(statObjectResponse.bucket())
                                .object(statObjectResponse.object())
                                .offset(startByte)
                                .length(contentLength)
                                .build());
                os = new BufferedOutputStream(response.getOutputStream());
                // 将读取的文件写入到OutputStream
                byte[] buffer = new byte[1024];
                long bytesWritten = 0;
                int bytesRead = -1;
                while ((bytesRead = stream.read(buffer)) != -1) {
                    if (bytesWritten + bytesRead > contentLength) {
                        os.write(buffer, 0, (int) (contentLength - bytesWritten));
                        break;
                    } else {
                        os.write(buffer, 0, bytesRead);
                        bytesWritten += bytesRead;
                    }
                }
                os.flush();
                response.flushBuffer();
                log.info("下载完毕");
                // 返回对应http状态
                responseEntity = new ResponseEntity<byte[]>(buffer, HttpStatus.OK);
            } finally {
                if (os != null) os.close();
                if (stream != null) stream.close();
            }
        }
        return responseEntity;
    }
}

测试链接:

下50M的情况

http://localhost:8082/download?filename=a9500aa2091875f3d02a9b84ae1ab712.mp4&range=bytes=0-52428800

 从50m再次下载50m

http://localhost:8082/download?filename=a9500aa2091875f3d02a9b84ae1ab712.mp4&range=bytes=52428800-104857600

都下载到了前端本地以后由客户端进行合并操作就好了。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你可以使用以下工具类来实现Spring BootMinIO结合进行文件分片下载: ```java import io.minio.MinioClient; import io.minio.ObjectStat; import io.minio.errors.*; import io.minio.messages.Part; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.List; @Component public class MinioFileDownloader { @Autowired private MinioClient minioClient; public void downloadFile(String bucketName, String objectName, String filePath) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InsufficientDataException, InternalException, InvalidResponseException, ErrorResponseException, XmlParserException, InvalidArgumentException { // 获取文件的元数据 ObjectStat objectStat = minioClient.statObject(bucketName, objectName); long fileSize = objectStat.length(); if (fileSize <= 0) { throw new IOException("File is empty"); } // 分片下载文件 int partSize = 5 * 1024 * 1024; // 分片大小为5MB long totalPartsCount = (long) Math.ceil((double) fileSize / partSize); for (int partNumber = 1; partNumber <= totalPartsCount; partNumber++) { long offset = (partNumber - 1) * partSize; long size = Math.min(partSize, fileSize - offset); InputStream inputStream = minioClient.getObject(bucketName, objectName, offset, size); try (BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream)) { byte[] buffer = new byte[(int) size]; bufferedInputStream.read(buffer, 0, (int) size); // 将分片写入文件 writeToFile(buffer, filePath); } } } private void writeToFile(byte[] data, String filePath) throws IOException { try (FileOutputStream outputStream = new FileOutputStream(filePath, true)) { outputStream.write(data); } } } ``` 你可以将以上代码添加到你的Spring Boot项目中,然后通过注入`MinioClient`来使用`MinioFileDownloader`类进行文件的分片下载。请确保已经正确配置了MinIO客户端以及相应的依赖包。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值