1.Minio介绍
MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
2.Springboot 中添加操作依赖
java
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.4.3</version>
</dependency>
3.yaml配置参数
java
#minio
minio:
endpoint:http://127.0.0.1:9000
accessKey:1293781k1wk1
secretKey:5oSbD@Z0yR1mm&NC8r*FeV!Htoxhd#O6f6h$JavDKuYQ
bucketName:jz07
4.MinioConfigpe配置
java
uration
@Getter
@RefreshScope
@Data
public class MinioConfig {
/**
* 连接url
*/
@Value("${minio.endpoint}")
private String endpoint;
/**
* 用户名
*/
@Value("${minio.accessKey}")
private String accessKey;
/**
* 密码
*/
@Value("${minio.secretKey}")
private String secretKey;
/**
* 存储桶
*/
@Value("${minio.bucketName}")
private String bucketName;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
}
5.Minio工具类
java
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.annotation.Resource;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* Minio工具类
*
* @author smr
*/
@Component
@Data
@Slf4j
public class MinioUtils {
private final MinioClient minioClient;
private static final int BUFFER_SIZE = 1024 * 1024 * 4;
@Resource
private MinioConfig minioConfig;
@Autowired
public MinioUtils(MinioClient minioClient) {
this.minioClient = minioClient;
}
/**
* 检验桶
*/
public Boolean bucketExists(String bucketName) {
try {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
log.error("检验BucketName存在失败", e);
return false;
}
}
/**
* 创建桶
*/
public void makeBucket(String bucketName) {
try {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
log.error("检验BucketName存在失败", e);
}
}
/**
* 上传Multipart文件
*/
public void uploadFileMultipartFile(String objectName, MultipartFile file) throws Exception {
// 使用 PutObjectArgs 进行文件上传
minioClient.putObject(
PutObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build());
}
/**
* 上传流文件
*/
public void uploadFileStream(String bucketName, String objectName, FileInputStream stream, String contentType) throws Exception {
// 使用 PutObjectArgs 进行文件上传
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(stream, stream.getChannel().size(), -1)
.contentType(contentType)
.build());
}
/**
* 上传本地文件
*/
public void uploadLocalFile(String bucketName, String objectName, File file) throws Exception {
UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.filename(file.getAbsolutePath())
.build();
minioClient.uploadObject(uploadObjectArgs);
}
/**
* 下载文件
*/
public void downloadFile(String bucketName, String objectName, String downloadPath) throws Exception {
InputStream stream = minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
// 使用缓冲流写入文件
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(downloadPath))) {
byte[] buf = new byte[1024];
int len;
while ((len = stream.read(buf)) != -1) {
bos.write(buf, 0, len);
}
}
stream.close();
}
/**
* 获取文件流
*/
public InputStream getFileInputStream(String bucketName, String objectName) throws Exception {
return minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
}
public void downloadWithPar(String fileId, OutputStream outputStream) {
log.info("downloadWithPar begin fileId [{}]", fileId);
isFileExist(fileId);
try {
StopWatch downLoadStopWatch = new StopWatch();
downLoadStopWatch.start();
InputStream inputStream = minioClient.getObject(
GetObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(fileId)
.build());
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
long fileSize = 0;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
outputStream.flush();
fileSize += bytesRead;
}
outputStream.flush();
inputStream.close();
downLoadStopWatch.stop();
log.info("download finished. fileId: [{}], file size: [{}], download time: [{}] ms",
fileId, FileUtils.byteCountToDisplaySize(fileSize),
downLoadStopWatch.getTotalTimeMillis());
} catch (Exception e) {
log.error("Failed to download the file. fileId: [{}]", fileId, e);
throw new RuntimeException("Failed to download the file from the Minio platform", e);
);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 检查文件是否存在
*/
public boolean isFileExist(String objectName) {
try {
minioClient.statObject(
StatObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(objectName)
.build());
return true;
} catch (Exception e) {
return false;
}
}
/**
* 删除文件
*/
public void deleteFile(String objectName) throws Exception {
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(objectName)
.build());
}
/**
* 列出存储桶中的所有文件
*/
public List<String> listFiles(String bucketName) throws Exception {
List<String> files = new ArrayList<>();
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder()
.bucket(bucketName)
.build());
for (Result<Item> result : results) {
files.add(result.get().objectName());
}
return files;
}
/**
* 获取文件的下载URL
*/
public String getPath(String objectName) throws Exception {
return minioConfig.getBucketName() + "/" + objectName;
}
/**
* 获取文件的下载URL
*/
public String getDownloadUrl(String objectName) throws Exception {
return minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.bucket(minioConfig.getBucketName())
.object(objectName)
.method(Method.GET)
.build()
);
}
/**
* 下载文件的部分数据
*
* @param bucketName 存储桶名称
* @param objectName 对象名称
* @param offset 起始字节位置
* @param length 要下载的字节长度
* @return 部分文件数据的字节数组
*/
public byte[] downloadPartialData(String bucketName, String objectName, long offset, long length) throws Exception {
try (InputStream stream = minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.offset(offset)
.length(length)
.build())) {
byte[] buffer = new byte[(int) length];
stream.read(buffer);
return buffer;
}
}
/**
* 文件转换
*
* @param file
* @return MultipartFile
*/
public MultipartFile convert(File file) {
DiskFileItem fileItem = new DiskFileItem("file", "application/octet-stream", false, file.getName(), (int) file.length(), file.getParentFile());
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fileItem.getOutputStream().write(buffer, 0, bytesRead);
}
} catch (Exception e) {
throw new RuntimeException("Error converting MultipartFile to File", e);
}
return new CommonsMultipartFile(fileItem);
}
}
6.FileService调用工具类
java
@Service
@Slf4j
public class FieldService {
@Resource
private MinioUtils minioUtils;
/**
* 文件上传
*
* @param file
* @return
*/
public UploadFileRSP uploadPic(MultipartFile file) {
log.info("文件上传开始:{}", file.getOriginalFilename());
//首先判断文件是否存在
if (minioUtils.isFileExist(file.getOriginalFilename())) {
throw new ServiceException("该文件名已存在,请修改文件名后上传");
}
UploadFileRSP rsp = new UploadFileRSP();
try {
minioUtils.uploadFileMultipartFile(file.getOriginalFilename(), file);
rsp.setFileName(file.getOriginalFilename());
rsp.setFilePath(minioUtils.getPath(rsp.getFileName()));
rsp.setFileUrl(minioUtils.getDownloadUrl(rsp.getFileName()));
return rsp;
} catch (Exception e) {
log.error("出海咨询模块-文件上传失败", e);
throw new ServiceException(ResultCode.INTERNAL_SERVER_ERROR, "文件上传失败。" + e.getMessage());
}
}
/**
* 文件下载
*/
public void downloadPic(String fileName, HttpServletResponse response) {
try {
if(!minioUtils.isFileExist(fileName)){
throw new ServiceException(ResultCode.INTERNAL_SERVER_ERROR, "文件不存在。");
}
response.reset();
response.setContentType("application/octet-stream");
String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-Disposition",
"attachment; filename=" + encodedFileName + "; filename*=utf-8''" + encodedFileName);
minioUtils.downloadWithPar(fileName, response.getOutputStream());
log.info("下载附件成功。");
} catch (Exception e) {
log.error("附件下载失败。fileName,[{}]", fileName, e);
throw new ServiceException(ResultCode.INTERNAL_SERVER_ERROR, "附件下载失败。" + e.getMessage());
}
}
/**
* 删除文件
*/
public Boolean deleteFile(String fileName) {
Assert.notNull(fileName, "文件名不能为空");
try {
if (minioUtils.isFileExist(fileName)) {
minioUtils.deleteFile(fileName);
return true;
} else {
throw new ServiceException(ResultCode.INTERNAL_SERVER_ERROR, "文件不存在无法删除。");
}
} catch (Exception e) {
log.error("附件删除失败。fileName,[{}]", fileName, e);
throw new ServiceException(ResultCode.INTERNAL_SERVER_ERROR, "附件删除失败。" + e.getMessage());
}
}
/**
* 创建桶
*/
public void createBuk(String bucketName) {
try {
if(minioUtils.bucketExists(bucketName)){
log.info("BucketName已存在。");
} else {
minioUtils.makeBucket(bucketName);
}
} catch (Exception e) {
log.error("创建失败", e);
}
}
}