Java大文件分片上传(minio版),超详细

本文使用spring boot 结合minio文件服务做的大文件分片上传,思路:①:初始化文件调用后端接口,后端再调用minio把文件分片成几份,生成每个分片的minio上传url②:把提起分片好的文件依次调用上一把返回的url ③:调用合并分片文件接口,完成上传 PS:文件并未经过后端服务器,而是直接上传到minio文件服务的

文章目录

    • 第一步:pom引入minio
    • 第二步:配置minio
    • 第三步:自定义minio客户端与工具类
    • 1. 自定义客户端
    • 2.封装minio配置
    • 3.上传工具类
    • 4.service
    • 5.service实现
    • 6.接口控制器
    • 第四步:文件切片
    • 第五步:示例截图

第一步:pom引入minio

 <!--minio大文件上传--><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.0.3</version></dependency>

第二步:配置minio

# minio 配置(大文件分片上传使用)
minio:endpoint: ${MINIO_ENDPOINT:http://minio服务IP:端口号}accesskey: ${MINIO_ASSESSKEY:密钥}secretkey: ${MINIO_SECRETKEY:密匙}bucket: ${MINIO_BUCKET:桶名称}

在这里插入图片描述

第三步:自定义minio客户端与工具类

1. 自定义客户端

package com.gcddd.visual.uav.largeFileUpload.util;import com.google.common.collect.Multimap;
import io.minio.CreateMultipartUploadResponse;
import io.minio.ListPartsResponse;
import io.minio.MinioClient;
import io.minio.ObjectWriteResponse;
import io.minio.errors.*;
import io.minio.messages.Part;import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;/*** 自定义minio* @date 2023-09-15*/
public class CustomMinioClient extends MinioClient {public CustomMinioClient(MinioClient client) {super(client);}public String initMultiPartUpload(String bucket, String region, String object, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, ServerException, InternalException, XmlParserException, InvalidResponseException, ErrorResponseException {CreateMultipartUploadResponse response = this.createMultipartUpload(bucket, region, object, headers, extraQueryParams);return response.result().uploadId();}public ObjectWriteResponse mergeMultipartUpload(String bucketName, String region, String objectName, String uploadId, Part[] parts, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, ServerException, InternalException, XmlParserException, InvalidResponseException, ErrorResponseException {return this.completeMultipartUpload(bucketName, region, objectName, uploadId, parts, extraHeaders, extraQueryParams);}public ListPartsResponse listMultipart(String bucketName, String region, String objectName, Integer maxParts, Integer partNumberMarker, String uploadId, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {return this.listParts(bucketName, region, objectName, maxParts, partNumberMarker, uploadId, extraHeaders, extraQueryParams);}
}

2.封装minio配置

package com.gcddd.visual.uav.largeFileUpload.configurer;import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;/*** @Description: MinioProperties* minio配置类*/
@ConfigurationProperties(prefix = "minio")
@Getter
@Setter
public class MinioProperties {private String endpoint;private String accesskey;private String secretkey;private String bucket;
}

3.上传工具类

package com.gcddd.visual.uav.largeFileUpload.configurer;import cn.hutool.core.util.StrUtil;
import com.gcddd.visual.uav.largeFileUpload.util.CustomMinioClient;
import com.google.common.collect.HashMultimap;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Part;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;/**
* @Description: MinIoUtils* 工具类*/
@Component
@Configuration
@EnableConfigurationProperties({MinioProperties.class})
public class MinIoUtils {private final MinioProperties minioProperties;private CustomMinioClient customMinioClient;public MinIoUtils(MinioProperties minioProperties) {this.minioProperties = minioProperties;}@PostConstructpublic void init() {MinioClient minioClient = MinioClient.builder().endpoint(minioProperties.getEndpoint()).credentials(minioProperties.getAccesskey(), minioProperties.getSecretkey()).build();customMinioClient = new CustomMinioClient(minioClient);}/*** 单文件签名上传** @param objectName 文件全路径名称* @return /*/public String getUploadObjectUrl(String objectName) {try {return customMinioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.PUT).bucket(minioProperties.getBucket()).object(objectName).expiry(1, TimeUnit.DAYS)//.extraHeaders(headers).build());} catch (Exception e) {e.printStackTrace();return null;}}/***  初始化分片上传** @param objectName 文件全路径名称* @param partCount 分片数量* @param contentType 类型,如果类型使用默认流会导致无法预览* @return /*/public Map<String, Object> initMultiPartUpload(String objectName, int partCount, String contentType) {Map<String, Object> result = new HashMap<>();try {if (StrUtil.isBlank(contentType)) {contentType = "application/octet-stream";}HashMultimap<String, String> headers = HashMultimap.create();headers.put("Content-Type", contentType);String uploadId = customMinioClient.initMultiPartUpload(minioProperties.getBucket(), null, objectName, headers, null);result.put("uploadId", uploadId);List<String> partList = new ArrayList<>();Map<String, String> reqParams = new HashMap<>();//reqParams.put("response-content-type", "application/json");reqParams.put("uploadId", uploadId);for (int i = 1; i <= partCount; i++) {reqParams.put("partNumber", String.valueOf(i));String uploadUrl = customMinioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.PUT).bucket(minioProperties.getBucket()).object(objectName).expiry(1, TimeUnit.DAYS).extraQueryParams(reqParams).build());partList.add(uploadUrl);}result.put("uploadUrls", partList);} catch (Exception e) {e.printStackTrace();return null;}return result;}/*** 分片上传完后合并** @param objectName 文件全路径名称* @param uploadId 返回的uploadId* @return /*/public boolean mergeMultipartUpload(String objectName, String uploadId) {try {//TODO::目前仅做了最大1000分片Part[] parts = new Part[1000];ListPartsResponse partResult = customMinioClient.listMultipart(minioProperties.getBucket(), null, objectName, 1000, 0, uploadId, null, null);int partNumber = 1;for (Part part : partResult.result().partList()) {parts[partNumber - 1] = new Part(partNumber, part.etag());partNumber++;}customMinioClient.mergeMultipartUpload(minioProperties.getBucket(), null, objectName, uploadId, parts, null, null);} catch (Exception e) {e.printStackTrace();return false;}return true;}
}

4.service

package com.gcddd.visual.uav.largeFileUpload.service;import java.util.Map;/*** @date 2023-09-13*/
public interface UploadService {/*** 分片上传初始化** @param path        路径* @param filename    文件名* @param partCount   分片数量* @param contentType /* @return /*/Map<String, Object> initMultiPartUpload(String path, String filename, Integer partCount, String contentType);/*** 完成分片上传** @param objectName 文件名* @param uploadId 标识* @return /*/boolean mergeMultipartUpload(String objectName, String uploadId);
}

5.service实现

package com.gcddd.visual.uav.largeFileUpload.service.impl;import com.gcddd.visual.uav.largeFileUpload.configurer.MinIoUtils;
import com.gcddd.visual.uav.largeFileUpload.service.UploadService;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;import java.util.Map;/*** @date 2023-09-13*/
@Service
@RequiredArgsConstructor
public class UploadServiceImpl implements UploadService {private final MinIoUtils minIoUtils;@Overridepublic Map<String, Object> initMultiPartUpload(String path, String filename, Integer partCount, String contentType) {path = path.replaceAll("/+", "/");if (path.indexOf("/") == 0) {path = path.substring(1);}String filePath = path + "/" + filename;Map<String, Object> result;// 单文件,直接上传if (partCount == 1) {String uploadObjectUrl = minIoUtils.getUploadObjectUrl(filePath);result = ImmutableMap.of("uploadUrls", ImmutableList.of(uploadObjectUrl));} else {//多文件,分片上传result = minIoUtils.initMultiPartUpload(filePath, partCount, contentType);}return result;}@Overridepublic boolean mergeMultipartUpload(String objectName, String uploadId) {return minIoUtils.mergeMultipartUpload(objectName, uploadId);}
}

6.接口控制器

import cn.hutool.core.lang.Assert;
import cn.hutool.json.JSONObject;
import com.gcddd.visual.uav.largeFileUpload.service.UploadService;
import com.gcddd.visual.uav.util.DateUtils;
import com.google.common.collect.ImmutableMap;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;import java.util.Map;/*** 大文件上传控制器*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/largeFileUpload" )
@Tag(description = "largeFileUpload" , name = "大文件分片上传" )
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class uploadController {private final UploadService uploadService;//    1.用户调用初始化接口,后端调用minio初始化,得到uploadId,生成每个分片的minio上传url
//初始化 示例: $ curl --location --request POST 'http://127.0.0.1:9998/admin/largeFileUpload/multipart/init' \
//            --header 'Content-Type: application/json' \
//            --data-raw '{//            "filename": "result.png",//            "partCount": 2
//              }'//    返回示例:
//{
//    "uploadId": "eb163e3d-bbf5-48ae-aa56-85cf2a3c0fa3",
//        "uploadUrls": [
//    "http://ip:端口号/uav/20230913/result.png?uploadId=eb163e3d-bbf5-48ae-aa56-85cf2a3c0fa3&partNumber=1&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=lengleng%2F20230913%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230913T031427Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=9e7bd28d6f4d42d0ed447750cadcd8782ccc2648a66ecb25a1e7639593ad7dd6",
//            "http://ip:端口号/uav/20230913/result.png?uploadId=eb163e3d-bbf5-48ae-aa56-85cf2a3c0fa3&partNumber=2&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=lengleng%2F20230913%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230913T031427Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=60d31989b9dc3bdacf6bbc9c13cb5c982f79b1e3a0bbcb948e0ec90fede2f9f3"
//    ]
//}
//    2.用户调用对应分片的上传地址,多次上传会覆盖
//    示例:
//    curl --location --request PUT 'http://ip:端口号/uav/20230913/result.png?uploadId=eb163e3d-bbf5-48ae-aa56-85cf2a3c0fa3&partNumber=1&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=lengleng%2F20230913%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230913T031427Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=9e7bd28d6f4d42d0ed447750cadcd8782ccc2648a66ecb25a1e7639593ad7dd6' \
//            --header 'Content-Type: application/octet-stream' \
//            --data-binary '@/D:/jpgone'
//    curl --location --request PUT 'http://ip:端口号/uav/20230913/result.png?uploadId=eb163e3d-bbf5-48ae-aa56-85cf2a3c0fa3&partNumber=2&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=lengleng%2F20230913%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230913T031427Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=60d31989b9dc3bdacf6bbc9c13cb5c982f79b1e3a0bbcb948e0ec90fede2f9f3' \
//            --header 'Content-Type: application/octet-stream' \
//            --data-binary '@/D:/jpgtwo'
//    3.调用完成接口,后端查询所有上传的分片并合并,uploadId为第一步返回的uploadId
//    示例:
//    curl --location --request PUT '127.0.0.1:8006/multipart/complete' \
//            --header 'Content-Type: application/json' \
//            --data-raw '{//    "objectName":"20230913/result.png",//    "uploadId":"eb163e3d-bbf5-48ae-aa56-85cf2a3c0fa3"
//                      }'/*** 分片初始化** @param requestParam 请求参数* @return /*/@PostMapping("/multipart/init")public ResponseEntity<Object> initMultiPartUpload(@RequestBody JSONObject requestParam) {// 路径(存入minio的路径)String pathText = DateUtils.getTodays("yyyyMMdd");//当前日期String path = requestParam.getStr("path", pathText);// 文件名String filename = requestParam.getStr("filename", "test.obj");// content-typeString contentType = requestParam.getStr("contentType", "application/octet-stream");// md5-可进行秒传判断String md5 = requestParam.getStr("md5", "");// 分片数量Integer partCount = requestParam.getInt("partCount", 100);//TODO::业务判断+秒传判断Map<String, Object> result = uploadService.initMultiPartUpload(path, filename, partCount, contentType);return new ResponseEntity<>(result, HttpStatus.OK);}/*** 合并分片文件,完成上传** @param requestParam 用户参数* @return /*/@PutMapping("/multipart/complete")public ResponseEntity<Object> completeMultiPartUpload(@RequestBody JSONObject requestParam) {// 文件名完整路径String objectName = requestParam.getStr("objectName");//初始化返回的uploadIdString uploadId = requestParam.getStr("uploadId");Assert.notNull(objectName, "objectName must not be null");Assert.notNull(uploadId, "uploadId must not be null");boolean result = uploadService.mergeMultipartUpload(objectName, uploadId);return new ResponseEntity<>(ImmutableMap.of("success", result), HttpStatus.OK);}
}

第四步:文件切片

把result.png图片以500M大小切片,重命名为result.png.后缀

split -b 500m result.png result.png.

在这里插入图片描述

第五步:示例截图

这里测试为把result.png切为2片,返回了uploadId 和两个minio文件上传地址
在这里插入图片描述

分片上传的地址上传文件,请求方式为put
在这里插入图片描述

在这里插入图片描述
合并文件,完成上传
在这里插入图片描述
检查minio是否成功
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/110442.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言是否快被时代所淘汰?

今日话题&#xff0c;C语言是否快被时代所淘汰&#xff1f;在移动互联网的冲击下&#xff0c;windows做的人越来越少&#xff0c;WP阵营没人做&#xff0c;后台简单的php&#xff0c;复杂的大数据处理的java&#xff0c;要求性能的c。主流一二线公司基本上没多少用C#的了。其实…

防止泄露,保护隐私!如何清除你的谷歌搜索历史记录

按照以下说明学习如何从你的谷歌帐户、谷歌Chrome浏览器、谷歌iOS或Android应用程序或谷歌应用程序中删除你的谷歌历史记录。 如何从你的谷歌帐户中删除搜索历史记录 清除你的谷歌搜索历史并不意味着谷歌实际上会删除你的搜索数据。谷歌仍然会记录你如何以及何时使用某些功能…

Vue3+ElementUI使用

<!DOCTYPE html> <html> <head><meta charset"UTF-8"><meta name"viewport" content"initial-scale1.0,maximum-scale1.0,minimum-scale1.0,user-scalable0, widthdevice-width"/><!-- 引入样式 --><lin…

mysql内连接与外连接详解

内连接与外连接 内连接外连接 在数据库中&#xff0c;连接操作是一种把两个或者多个表的记录组合在一起的操作&#xff0c;常用的有内连接&#xff08;Inner Join&#xff09;、外连接&#xff08;Outer Join&#xff09;等。 内连接 内连接&#xff08;Inner Join&#xff0…

算法之排序

文章目录 前言一、二分查找1、正常二分2、二分找第一个出现要查找的数的位置3、在旋转数组中找最小数字二分总结 一、归并排序1、正常归并2、小和问题归并总结 前言 之前我们学习了各种排序算法&#xff0c;今天来让我们看看在线OJ上的题目吧&#xff0c;有哪些排序算法有什么…

Vue.js和TypeScript:如何完美结合

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

企业架构LNMP学习笔记55

MongoDB的安装和配置&#xff1a; 1、安装方式介绍&#xff1a; yum安装方式&#xff1a; 手动通用安装方式&#xff1a; 2、二进制可执行安装&#xff1a; 下载路径&#xff1a;Download MongoDB Community Server | MongoDB 下载mongodb的源码包进行安装。 1&#xff09;…

力扣236 补9.14

做不来&#xff0c;我做中等题基本上都是没有思路&#xff0c;这里需要先遍历祖先节点&#xff0c;那必然用先序遍历&#xff0c;这题还是官方题解容易理解&#xff0c;第二火的题解反而把我弄得脑袋昏昏的。 class Solution { TreeNode ans; public TreeNode lowestCommonAnce…

在线海报图片设计器、图片编辑器源码/仿照稿定设计源码

在线海报设计系统素材设计源码是一个漂亮且功能强大的在线海报图片设计器&#xff0c;仿照稿定设计而成。该系统适用于多种场景&#xff0c;包括海报图片生成、电商分享图、文章长图、视频/公众号封面等。用户无需下载软件&#xff0c;即可轻松实现创意&#xff0c;迅速完成排版…

C++之std::holds_alternative与std::get应用实例(二百一十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

梓航建站系统独立开源版企业官网建站v1.2.5

1、超级伪静态功能便于SEO 2、自定义导航菜单内容及连接(支持三级分类) 3、多种模型自由组合&#xff0c;灵活度更高 4、页面自由控制数据显示(分类更清晰) 5、43种图文布局让页面更酷炫 6、8种文章布局展示(文字也能多样化) 7、4种留言布局&#xff0c;表单也不单调了 …

初识Docker

文章目录 Docker安装Docker简介1.什么是虚拟化、容器化?2. 为什么需要虚拟化、容器化&#xff1f;3. 虚拟化的实现方式主机虚拟化的实现方式容器虚拟化实现 4. 虚拟机和Docker的区别 Docker安装 基于Centos7进行安装 1.确认系统版本和CPU架构&#xff0c;Centos7的x86_64架构…