1.配置minio MinioAutoConfiguration 類
- @AllArgsConstructor
- @EnableConfigurationProperties({MinioProperties.class})
- public class MinioAutoConfiguration {
- private final MinioProperties properties;
- @Bean
- @ConditionalOnMissingBean(MinioTemplate.class)
- @ConditionalOnProperty(name = "amc.minio.url")
- MinioTemplate minioTemplate() {
- return new MinioTemplate(
- properties.getUrl(),
- properties.getAccessKey(),
- properties.getSecretKey()
- );
- }
- }
2.屬性類
- @Data
- @ConfigurationProperties(prefix = "amc.minio")
- public class MinioProperties {
- /**
- * minio 服務地址 http://ip:port
- */
- private String url;
- /**
- * 用戶名
- */
- private String accessKey;
- /**
- * 密碼
- */
- private String secretKey;
- }
3.創建minioTemplate工具類
- package com.amc.minio.entity;
- import cn.hutool.core.util.StrUtil;
- import cn.hutool.core.util.URLUtil;
- import io.minio.*;
- import io.minio.errors.*;
- import io.minio.http.Method;
- import io.minio.messages.Bucket;
- import io.minio.messages.Item;
- import lombok.RequiredArgsConstructor;
- import lombok.SneakyThrows;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.InitializingBean;
- import org.springframework.util.Assert;
- import java.io.IOException;
- import java.io.InputStream;
- import java.security.InvalidKeyException;
- import java.security.NoSuchAlgorithmException;
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Optional;
- /**
- * minio 交互類
- * @author Administrator
- */
- @RequiredArgsConstructor
- @Slf4j
- public class MinioTemplate implements InitializingBean {
- private final String endpoint;
- private final String accessKey;
- private final String secretKey;
- private MinioClient client;
- /**
- * 創建bucket
- *
- * @param bucketName bucket名稱
- */
- @SneakyThrows
- public void createBucket(String bucketName) {
- if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
- client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
- }
- }
- /**
- * 合并分片文件成對象文件
- *
- * @param chunkBucKetName 分片文件所在存儲桶名稱
- * @param composeBucketName 合并后的對象文件存儲的存儲桶名稱
- * @param chunkNames 分片文件名稱集合
- * @param objectName 合并后的對象文件名稱
- * @return true/false
- */
- @SneakyThrows
- public boolean composeObject(String chunkBucKetName, String composeBucketName, List<String> chunkNames, String objectName) {
- if (null == chunkBucKetName) {
- chunkBucKetName = "chunk";
- }
- List<ComposeSource> sourceObjectList = new ArrayList<>(chunkNames.size());
- for (String chunk : chunkNames) {
- sourceObjectList.add(
- ComposeSource.builder()
- .bucket(chunkBucKetName)
- .object(chunk)
- .build()
- );
- }
- client.composeObject(
- ComposeObjectArgs.builder()
- .bucket(composeBucketName)
- .object(objectName)
- .sources(sourceObjectList)
- .build()
- );
- return true;
- }
- /**
- * 根據md5獲取所有分片文件名稱(minio的文件名稱 = 文件path)
- * @param bucketName 桶(文件夾)名稱
- * @param prefix 前綴
- * @return List<String>
- */
- @SneakyThrows
- public List<String> listObjectNames(String bucketName, String prefix) {
- List<String> chunkPaths = new ArrayList<>();
- ListObjectsArgs listObjectsArgs;
- if (StrUtil.isBlank(prefix)) {
- listObjectsArgs = ListObjectsArgs.builder()
- .bucket(bucketName)
- .recursive(true)
- .build();
- } else {
- listObjectsArgs = ListObjectsArgs.builder()
- .bucket(bucketName)
- .prefix(prefix)
- .recursive(true)
- .build();
- }
- Iterable<Result<Item>> chunks = client.listObjects(listObjectsArgs);
- Iterator<Result<Item>> iterator = chunks.iterator();
- while(iterator.hasNext()) {
- Result<Item> item = iterator.next();
- chunkPaths.add(URLUtil.decode(item.get().objectName()));
- }
- return chunkPaths;
- }
- /**
- * 通過文件名字判斷文件是否存在如果存在返回false否則返回true
- * @param bucketName 桶名稱
- * @param fileName 文件名稱
- * @return true|false
- */
- @SneakyThrows
- public Boolean fileExists(String bucketName, String fileName) {
- boolean exist = true;
- try {
- if(client.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build()) == null){
- exist = false;
- }
- } catch (Exception e) {
- exist = false;
- }
- return exist;
- }
- /**
- * 獲取全部bucket
- * <p>
- * https://docs./cn/java-client-api-reference.html#listBuckets
- */
- @SneakyThrows
- public List<Bucket> getAllBuckets() {
- return client.listBuckets();
- }
- /**
- * @param bucketName bucket名稱
- */
- @SneakyThrows
- public Optional<Bucket> getBucket(String bucketName) {
- return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
- }
- /**
- * @param bucketName bucket名稱
- */
- @SneakyThrows
- public Boolean bucketExists(String bucketName) {
- return client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
- }
- /**
- * @param bucketName bucket名稱
- */
- @SneakyThrows
- public void removeBucket(String bucketName) {
- client.removeBucket(RemoveBucketArgs.builder()
- .bucket(bucketName)
- .build());
- }
- /**
- * 根據文件前置查詢文件
- *
- * @param bucketName bucket名稱
- * @param prefix 前綴
- * @param recursive 是否遞歸查詢
- * @return MinioItem 列表
- */
- @SneakyThrows
- public List<MinioItem> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
- List<MinioItem> objectList = new ArrayList<>();
- Iterable<Result<Item>> objectsIterator = client.listObjects(ListObjectsArgs.builder()
- .bucket(bucketName)
- .prefix(prefix)
- .recursive(recursive)
- .build());
- for (Result<Item> itemResult : objectsIterator) {
- if(itemResult.get() != null) {
- objectList.add(new MinioItem(itemResult.get()));
- }
- }
- return objectList;
- }
- /**
- * 獲取文件外鏈
- *
- * @param bucketName bucket名稱
- * @param objectName 文件名稱
- * @param expires 過期時間 <=7
- * @return url
- */
- @SneakyThrows
- public String getObjectUrl(String bucketName, String objectName, Integer expires) {
- return client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
- .method(Method.GET)
- .bucket(bucketName)
- .object(objectName)
- .expiry(expires).build());
- }
- /**
- * 創建上傳文件對象的外鏈
- *
- * @param bucketName 存儲桶名稱
- * @param objectName 欲上傳文件對象的名稱
- * @param expiry 過期時間(分鐘) 最大為7天 超過7天則默認最大值
- * @return uploadUrl
- */
- @SneakyThrows
- public String createUploadUrl(String bucketName, String objectName, Integer expiry) {
- return client.getPresignedObjectUrl(
- GetPresignedObjectUrlArgs.builder()
- .method(Method.PUT)
- .bucket(bucketName)
- .object(objectName)
- .expiry(expiry)
- .build()
- );
- }
- /**
- * 獲取文件
- *
- * @param bucketName bucket名稱
- * @param objectName 文件名稱
- * @return 二進制流
- */
- @SneakyThrows
- public InputStream getObject(String bucketName, String objectName) {
- return client.getObject(GetObjectArgs.builder()
- .bucket(bucketName)
- .object(objectName).build());
- }
- /**
- * 從指定桶中獲取對象的前綴輸入流
- *
- * @param bucketName 桶名
- * @param objectName 對象名
- * @return 讀取對象前1024字節的輸入流,如果發生異常則返回null
- */
- public InputStream getPerfixInputStream(String bucketName, String objectName){
- // 讀取文件的前1024字節
- // 你可以根據需要調整這個值
- long size = 1024;
- try {
- return client.getObject(GetObjectArgs.builder()
- .bucket(bucketName)
- // 文件的起始位置
- .offset(0L)
- // 需要讀取的字節數
- .length(size)
- .object(objectName).build());
- } catch (Exception e) {
- log.error(e.getMessage(),e);
- }
- return null;
- }
- /**
- * 下載到指定位置
- *
- * @param bucketName bucket名稱
- * @param objectName 對象名稱
- * @param objectName 文件名稱
- * @return 二進制流
- */
- @SneakyThrows
- public void downloadObject(String bucketName, String objectName, String fileName) {
- client.downloadObject(DownloadObjectArgs.builder()
- .bucket(bucketName)
- .object(objectName)
- //必須指定文件名
- .filename(fileName)
- .build());
- }
- /**
- * 復制文件
- *
- * @param srcBucketName Source bucket name
- * @param srcObjectName Source object name
- * @param bucketName 目標 bucket名稱
- * @param objectName 目標 文件名稱
- * @return 二進制流
- */
- @SneakyThrows
- public void copyObject(String srcBucketName, String srcObjectName, String bucketName, String objectName) {
- client.copyObject(CopyObjectArgs.builder()
- .source(CopySource.builder()
- .bucket(srcBucketName)
- .object(srcObjectName)
- .build())
- .bucket(bucketName)
- .object(objectName)
- .build());
- }
- /**
- * 上傳文件
- *
- * @param bucketName bucket名稱
- * @param objectName 文件名稱
- * @param stream 文件流
- * @throws Exception https://docs./cn/java-client-api-reference.html#putObject
- */
- public void putObject(String bucketName, String objectName, InputStream stream) throws Exception {
- putObject(bucketName, objectName, stream, (long) stream.available(), "application/octet-stream");
- }
- /**
- * 上傳文件
- *
- * @param bucketName bucket名稱
- * @param objectName 文件名稱
- * @param stream 文件流
- * @param size 大小
- * @param contextType 類型
- * @throws Exception https://docs./cn/java-client-api-reference.html#putObject
- */
- public void putObject(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception {
- client.putObject(
- PutObjectArgs.builder()
- .bucket(bucketName)
- .object(objectName).stream(stream, size, -1)
- .contentType(contextType)
- .build());
- }
- /**
- * 獲取文件信息
- *
- * @param bucketName bucket名稱
- * @param objectName 文件名稱
- * @throws Exception https://docs./cn/java-client-api-reference.html#statObject
- */
- /*public ObjectStat getObjectInfo(String bucketName, String objectName) throws Exception {
- return client.statObject(
- StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
- }*/
- /**
- * 刪除文件
- *
- * @param bucketName bucket名稱
- * @param objectName 文件名稱
- */
- @SneakyThrows
- public void removeObject(String bucketName, String objectName) {
- client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
- }
- /**
- * 刪除文件
- *
- * @param bucketName bucket名稱
- * @param fileNames 文件名稱列表
- * @throws Exception https://docs./cn/java-client-api-reference.html#removeObject
- */
- @SneakyThrows
- public void removeObjects(String bucketName, List<String> fileNames) {
- fileNames.forEach(objectName -> {
- try {
- client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
- } catch (Exception e) {
- log.error(e.getMessage(),e);
- }
- });
- }
- /**
- * 刪除文件
- *
- * @param bucketName bucket名稱
- * @param fileNamePre 標識符
- * @throws Exception https://docs./cn/java-client-api-reference.html#removeObject
- */
- @SneakyThrows
- public void removeObjects(String bucketName, String fileNamePre) {
- final List<String> listObjectNames = this.listObjectNames(bucketName, fileNamePre);
- this.removeObjects(bucketName,listObjectNames);
- }
- @Override
- public void afterPropertiesSet() throws Exception {
- Assert.hasText(endpoint, "Minio url 為空");
- Assert.hasText(accessKey, "Minio accessKey為空");
- Assert.hasText(secretKey, "Minio secretKey為空");
- this.client = MinioClient.builder()
- .endpoint(endpoint)
- .credentials(accessKey, secretKey)
- .build();
- }
- }
4.初始化桶類
- @Slf4j
- public class MinioBucketInit implements Runnable{
- /**
- 算法文件存放的minio桶
- */
- public String[] buckets;
- private MinioTemplate minioTemplate;
- public MinioBucketInit(MinioTemplate minioTemplate,String[] buckets) {
- this.minioTemplate = minioTemplate;
- this.buckets = buckets;
- }
- @Override
- public void run() {
- if(buckets==null) {
- return;
- }
- for (int i = 0; i <buckets.length ; i++) {
- //判斷桶是否存在
- Boolean bucketNameExit = minioTemplate.bucketExists(buckets[i]);
- if(!bucketNameExit){
- minioTemplate.createBucket(buckets[i]);
- }
- }
- }
- }
5.使用
- log.debug("初始化minio的桶");
- //判斷有沒有桶
- threadPoolTaskExecutor.execute(new MinioBucketInit(minioTemplate,new String[]{bucketName}));
- //獲取源文件
- InputStream inputStream = minioTemplate.getObject(attachment.getBucketName(), attachment.getFilePath());
- //判斷文件是否存在
- Boolean fileExists = minioTemplate.fileExists(attachment.getBucketName(), attachment.getFilePath());
- //上傳文件到minio
- minioTemplate.putObject(MinioBucketInit.ALGORITH_MBUCKET, path, inputStream);
6.注意
1.上傳時如果流是空的,不上傳不成功的