下载客户端
https://dl.min.io/server/minio/release/windows-amd64/
在exe所在文件夹新建bucket目录
新建桶,开启读写权限
Springboot集成(文末有完整版依赖)
引入依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!--图片压缩-->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
<!--文件存储-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.3.4</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.8.1</version>
<scope>compile</scope>
</dependency>
配置类
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
public class MinIoConfig {
@Value("${min.io.endpoint}")
private String endpoint;
@Value("${min.io.accessKey}")
private String accessKey;
@Value("${min.io.secretKey}")
private String secretKey;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
}
yml配置
server:
port: 9100
min:
io:
endpoint: http://127.0.0.1:9000
accessKey: minioadmin
secretKey: minioadmin
spring:
servlet:
multipart:
max-file-size: 5MB
max-request-size: 15MB
DTO
import lombok.Data;
import java.io.Serializable;
@Data
public class MinIoUploadResDTO implements Serializable {
private static final long serialVersionUID = 475040120689218785L;
private String minFileName;
private String minFileUrl;
public MinIoUploadResDTO(String minFileName,String minFileUrl) {
this.minFileName = minFileName;
this.minFileUrl = minFileUrl;
}
import java.io.Serializable;
public class Result<T> implements Serializable {
private static final long serialVersionUID = 6273326371984994386L;
private Integer code;
private String msg;
private T data;
private Result() {
this.code = 200;
this.msg = "OK";
}
private Result(T data) {
this.code = 200;
this.msg = "OK";
this.setData(data);
}
private Result(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
private Result(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public Result<T> setError(Integer code, String msg) {
this.setCode(code);
this.setMsg(msg);
return this;
}
public boolean isSuccess() {
return this.getCode().equals(200);
}
public static Result ok() {
return new Result();
}
public static <T> Result ok(T data) {
return new Result(data);
}
public static <T> Result ok(Integer code, String msg) {
return new Result(code, msg);
}
public static <T> Result ok(Integer code, String msg, T data) {
return new Result(code, msg, data);
}
public static <T> Result error() {
return new Result(500, "failed");
}
public static <T> Result error(String msg) {
return new Result(500, msg);
}
public static <T> Result error(Integer code, String msg) {
return new Result(code, msg);
}
public static <T> Result error(Integer code, String msg, T data) {
return new Result(code, msg, data);
}
public Integer getCode() {
return this.code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return this.msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return this.data;
}
public void setData(T data) {
this.data = data;
}
}
客户端封装工具类
import com.aiko.miniotest.dto.MinIoUploadResDTO;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
@Component
public class MinIoUtils {
@Resource
private MinioClient client;
private static final String SEPARATOR_DOT = ".";
private static final String SEPARATOR_ACROSS = "-";
private static final String SEPARATOR_STR = "";
private static final String chunkBucKet = "img";
public final static boolean NOT_SORT = false;
public final static boolean SORT = true;
private final static Integer DEFAULT_EXPIRY = 60;
public final static boolean DELETE_CHUNK_OBJECT = true;
public final static boolean NOT_DELETE_CHUNK_OBJECT = false;
public boolean bucketExists(String bucketName) {
try {
return client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public void makeBucket(String bucketName) {
try {
boolean isExist = bucketExists(bucketName);
if (!isExist) {
client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public List<String> listBucketNames() {
List<Bucket> bucketList = listBuckets();
List<String> bucketListName = new ArrayList<>();
for (Bucket bucket : bucketList) {
bucketListName.add(bucket.name());
}
return bucketListName;
}
@SneakyThrows
private List<Bucket> listBuckets() {
return client.listBuckets();
}
public List<String> listObjectNames(String bucketName, String prefix) {
return listObjectNames(bucketName, prefix, NOT_SORT);
}
@SneakyThrows
public List<String> listObjectNames(String bucketName, String prefix, Boolean sort) {
boolean flag = bucketExists(bucketName);
if (flag) {
ListObjectsArgs listObjectsArgs;
if (null == 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);
List<String> chunkPaths = new ArrayList<>();
for (Result<Item> item : chunks) {
chunkPaths.add(item.get().objectName());
}
if (sort) {
chunkPaths.sort(new Str2IntComparator(false));
}
return chunkPaths;
}
return new ArrayList<>();
}
@SneakyThrows
public String createDirectory(String bucket, String WotDir) {
if (!this.bucketExists(bucket)) {
return null;
}
client.putObject(PutObjectArgs.builder().bucket(bucket).object(WotDir).stream(
new ByteArrayInputStream(new byte[]{}), 0, -1)
.build());
return WotDir;
}
@SneakyThrows
public boolean removeObject(String bucketName, String objectName) {
if (!bucketExists(bucketName)) {
return false;
}
client.removeObject(
RemoveObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
return true;
}
@SneakyThrows
public List<String> removeObjects(String bucketName, List<String> objectNames) {
if (!bucketExists(bucketName)) {
return new ArrayList<>();
}
List<DeleteObject> deleteObjects = new ArrayList<>(objectNames.size());
for (String objectName : objectNames) {
deleteObjects.add(new DeleteObject(objectName));
}
List<String> deleteErrorNames = new ArrayList<>();
Iterable<Result<DeleteError>> results = client.removeObjects(
RemoveObjectsArgs.builder()
.bucket(bucketName)
.objects(deleteObjects)
.build());
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
deleteErrorNames.add(error.objectName());
}
return deleteErrorNames;
}
@SneakyThrows
public String getObjectUrl(String bucketName, String objectName, Integer expiry) {
expiry = expiryHandle(expiry);
return client.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(objectName)
.expiry(expiry)
.build()
);
}
public String createUploadUrl(String bucketName, String objectName) {
return createUploadUrl(bucketName, objectName, DEFAULT_EXPIRY);
}
@SneakyThrows
public String createUploadUrl(String bucketName, String objectName, Integer expiry) {
expiry = expiryHandle(expiry);
return client.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.PUT)
.bucket(bucketName)
.object(objectName)
.expiry(expiry)
.build()
);
}
public void downLoadFile(HttpServletResponse response,String fileName) {
try(InputStream ism = new BufferedInputStream(client.getObject(GetObjectArgs.builder()
.bucket(chunkBucKet)
.object(fileName).build()))) {
client.statObject(StatObjectArgs.builder()
.bucket(chunkBucKet)
.object(fileName).build());
byte buf[] = new byte[1024];
int length = 0;
response.reset();
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
response.setContentType("application/x-msdownload");
response.setCharacterEncoding("utf-8");
OutputStream osm = new BufferedOutputStream(response.getOutputStream());
while ((length = ism.read(buf))>0) {
osm.write(buf,0, length);
}
osm.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
@SneakyThrows
public List<String> downLoadMore(String bucket, String directory) {
Iterable<Result<Item>> objs = client.listObjects(ListObjectsArgs.builder().bucket(bucket).prefix(directory).useUrlEncodingType(false).build());
List<String> list = new ArrayList<>();
for (Result<Item> result : objs) {
String objectName = null;
objectName = result.get().objectName();
StatObjectResponse statObject = client.statObject(StatObjectArgs.builder().bucket(bucket).object(objectName).build());
if (statObject != null && statObject.size() > 0) {
String fileurl = client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucket).object(statObject.object()).method(Method.GET).build());
list.add(fileurl);
}
}
return list;
}
public MinIoUploadResDTO upload(MultipartFile multipartFile, String bucketName, String directory) throws Exception {
if (!this.bucketExists(bucketName)) {
this.makeBucket(bucketName);
}
InputStream inputStream = multipartFile.getInputStream();
directory = Optional.ofNullable(directory).orElse("");
String minFileName = directory + minFileName(multipartFile.getOriginalFilename());
System.out.println(minFileName);
client.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(minFileName)
.contentType(multipartFile.getContentType())
.stream(inputStream, inputStream.available(), -1)
.build());
inputStream.close();
return new MinIoUploadResDTO(minFileName, getObjectUrl(bucketName, minFileName, DEFAULT_EXPIRY));
}
public void download(HttpServletResponse response, String bucketName, String minFileName) {
InputStream fileInputStream = null;
try {
fileInputStream = client.getObject(GetObjectArgs.builder()
.bucket(bucketName)
.object(minFileName).build());
response.setHeader("Content-Disposition", "attachment;filename=" + minFileName);
response.setContentType("application/force-download");
response.setCharacterEncoding("UTF-8");
IOUtils.copy(fileInputStream, response.getOutputStream());
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (InsufficientDataException e) {
e.printStackTrace();
} catch (InternalException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (ServerException e) {
e.printStackTrace();
} catch (XmlParserException e) {
e.printStackTrace();
} finally {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public List<String> createUploadChunkUrlList(String bucketName, String objectMD5, Integer chunkCount) {
if (null == bucketName) {
bucketName = chunkBucKet;
}
if (null == objectMD5) {
return null;
}
objectMD5 += "/";
if (null == chunkCount || 0 == chunkCount) {
return null;
}
List<String> urlList = new ArrayList<>(chunkCount);
for (int i = 1; i <= chunkCount; i++) {
String objectName = objectMD5 + i + ".chunk";
urlList.add(createUploadUrl(bucketName, objectName, DEFAULT_EXPIRY));
}
return urlList;
}
public String createUploadChunkUrl(String bucketName, String objectMD5, Integer partNumber) {
if (null == bucketName) {
bucketName = chunkBucKet;
}
if (null == objectMD5) {
return null;
}
objectMD5 += "/" + partNumber + ".chunk";
return createUploadUrl(bucketName, objectMD5, DEFAULT_EXPIRY);
}
public List<String> listChunkObjectNames(String bucketName, String ObjectMd5) {
if (null == bucketName) {
bucketName = chunkBucKet;
}
if (null == ObjectMd5) {
return null;
}
return listObjectNames(bucketName, ObjectMd5, SORT);
}
public Map<Integer, String> mapChunkObjectNames(String bucketName, String ObjectMd5) {
if (null == bucketName) {
bucketName = chunkBucKet;
}
if (null == ObjectMd5) {
return null;
}
List<String> chunkPaths = listObjectNames(bucketName, ObjectMd5);
if (null == chunkPaths || chunkPaths.size() == 0) {
return null;
}
Map<Integer, String> chunkMap = new HashMap<>(chunkPaths.size());
for (String chunkName : chunkPaths) {
Integer partNumber = Integer.parseInt(chunkName.substring(chunkName.indexOf("/") + 1, chunkName.lastIndexOf(".")));
chunkMap.put(partNumber, chunkName);
}
return chunkMap;
}
@SneakyThrows
public boolean composeObject(String chunkBucKetName, String composeBucketName, List<String> chunkNames, String objectName, boolean isDeleteChunkObject) {
if (null == chunkBucKetName) {
chunkBucKetName = chunkBucKet;
}
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()
);
if (isDeleteChunkObject) {
removeObjects(chunkBucKetName, chunkNames);
}
return true;
}
public boolean composeObject(String bucketName, List<String> chunkNames, String objectName) {
return composeObject(chunkBucKet, bucketName, chunkNames, objectName, NOT_DELETE_CHUNK_OBJECT);
}
public boolean composeObject(String bucketName, List<String> chunkNames, String objectName, boolean isDeleteChunkObject) {
return composeObject(chunkBucKet, bucketName, chunkNames, objectName, isDeleteChunkObject);
}
public boolean composeObjectAndRemoveChunk(String bucketName, List<String> chunkNames, String objectName) {
return composeObject(chunkBucKet, bucketName, chunkNames, objectName, DELETE_CHUNK_OBJECT);
}
private String minFileName(String originalFileName) {
String suffix = originalFileName;
if (originalFileName.contains(SEPARATOR_DOT)) {
suffix = originalFileName.substring(originalFileName.lastIndexOf(SEPARATOR_DOT));
}
return UUID.randomUUID().toString().replace(SEPARATOR_ACROSS, SEPARATOR_STR).toUpperCase() + suffix;
}
private static int expiryHandle(Integer expiry) {
expiry = expiry * 60;
if (expiry > 604800) {
return 604800;
}
return expiry;
}
static class Str2IntComparator implements Comparator<String> {
private final boolean reverseOrder;
public Str2IntComparator(boolean reverseOrder) {
this.reverseOrder = reverseOrder;
}
@Override
public int compare(String arg0, String arg1) {
Integer intArg0 = Integer.parseInt(arg0.substring(arg0.indexOf("/") + 1, arg0.lastIndexOf(".")));
Integer intArg1 = Integer.parseInt(arg1.substring(arg1.indexOf("/") + 1, arg1.lastIndexOf(".")));
if (reverseOrder) {
return intArg1 - intArg0;
} else {
return intArg0 - intArg1;
}
}
}
}
controller测试
import com.aiko.miniotest.dto.Result;
import com.aiko.miniotest.util.MinIoUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
@RestController
@RequestMapping("/minio")
public class MinIoController {
@Resource
private MinIoUtils minIoUtils;
private static final String MINIO_BUCKET = "img";
@RequestMapping(value = "/upload", method = RequestMethod.POST)
public Result upload(@RequestParam(value = "files") MultipartFile files){
try {
return Result.ok(minIoUtils.upload(files,MINIO_BUCKET,null));
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
@GetMapping("/download")
public void download(@RequestParam("minFileName")String minFileName,HttpServletResponse response){
minIoUtils.download(response,"img",minFileName);
}
}
Postman测试
上传
成功!!
返回的文件名并不是上传时候的文件名,是minio自动生成的;
minio 查看
下载
下载成功!!
完整版依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.12</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aiko</groupId>
<artifactId>minio-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>minio-test</name>
<description>minio-test</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!--图片压缩-->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
<!--文件存储-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.3.4</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.8.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
更多相关的api可以看这里
https://www.bookstack.cn/read/MinioCookbookZH/22.md
声明: 本文部分内容来自博客 https://blog.csdn.net/qq_41604890/article/details/114310259?spm=1001.2101.3001.6650.5&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-5.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-5.pc_relevant_aa&utm_relevant_index=10
侵删!!
|