<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.1.0</version>
</dependency>
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
@ApiModelProperty("endPoint是一个URL,域名,IPv4或者IPv6地址")
private String endpoint;
@ApiModelProperty("TCP/IP端口号")
private int port;
@ApiModelProperty("accessKey类似于用户ID,用于唯一标识你的账户")
private String accessKey;
@ApiModelProperty("secretKey是你账户的密码")
private String secretKey;
@ApiModelProperty("如果是true,则用的是https而不是http,默认值是true")
private Boolean secure;
@ApiModelProperty("默认存储桶")
private String bucketName;
@ApiModelProperty("配置目录")
private String configDir;
@ApiModelProperty("网关")
private String gateway;
private CustomMinioClient minioClient;
@Bean
public CustomMinioClient getMinioClient() {
try {
MinioClient minioClient = CustomMinioClient.builder()
.endpoint(endpoint, port, secure)
.credentials(accessKey, secretKey)
.build();
} catch (Exception e) {
e.printStackTrace();
}
return minioClient;
}
}
minio:
endpoint: {个人部署的服务器IP}
port: 9000
accessKey: minioadmin
secretKey: minioadmin
secure: false
bucketName: "oss-test"
configDir: "/home/data/"
gateway: "https://{个人部署的服务器IP}:9000/oss-test"
spring:
servlet:
# 附件上传限制大小
multipart:
enabled: true
# 单个文件的最大值
max-file-size: 500MB
# 最大请求文件的大小
max-request-size: 500MB
file-size-threshold: 500MB
@Slf4j
@Component
public class MinioUtil {
@Autowired
private MinioConfig minioConfig;
@Autowired
private CustomMinioClient minioClient;
private static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 3600;
/**
* url分隔符
*/
public static final String URI_DELIMITER = "/";
private static int RETRY_NUM = 3;
/**
* 检查存储桶是否存在
*
* @param bucketName 存储桶名称
* @return
*/
@SneakyThrows
public boolean bucketExists(String bucketName) {
boolean flag = false;
BucketExistsArgs bArgs = BucketExistsArgs.builder().bucket(bucketName).build();
flag = minioClient.bucketExists(bArgs);
if (flag) {
return true;
}
return false;
}
/**
* 创建存储桶
*
* @param bucketName 存储桶名称
*/
@SneakyThrows
public boolean makeBucket(String bucketName) {
boolean flag = bucketExists(bucketName);
if (!flag) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
return true;
} else {
return false;
}
}
/**
* 列出所有存储桶名称
*
* @return
*/
@SneakyThrows
public List<String> listBucketNames() {
List<Bucket> bucketList = listBuckets();
List<String> bucketListName = new ArrayList<>();
for (Bucket bucket : bucketList) {
bucketListName.add(bucket.name());
}
return bucketListName;
}
/**
* 列出所有存储桶
*
* @return
*/
@SneakyThrows
public List<Bucket> listBuckets() {
return minioClient.listBuckets();
}
/**
* 删除存储桶
*
* @param bucketName 存储桶名称
* @return
*/
@SneakyThrows
public boolean removeBucket(String bucketName) {
boolean flag = bucketExists(bucketName);
if (flag) {
Iterable<Result<Item>> myObjects = listObjects(bucketName);
for (Result<Item> result : myObjects) {
Item item = result.get();
// 有对象文件,则删除失败
if (item.size() > 0) {
return false;
}
}
// 删除存储桶,注意,只有存储桶为空时才能删除成功。
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
flag = bucketExists(bucketName);
if (!flag) {
return true;
}
}
return false;
}
/**
* 列出存储桶中的所有对象名称
*
* @param bucketName 存储桶名称
* @return
*/
@SneakyThrows
public List<String> listObjectNames(String bucketName) {
List<String> listObjectNames = new ArrayList<>();
boolean flag = bucketExists(bucketName);
if (flag) {
Iterable<Result<Item>> myObjects = listObjects(bucketName);
for (Result<Item> result : myObjects) {
Item item = result.get();
listObjectNames.add(item.objectName());
}
}
return listObjectNames;
}
/**
* 列出存储桶中的所有对象
*
* @param bucketName 存储桶名称
* @return
*/
@SneakyThrows
public Iterable<Result<Item>> listObjects(String bucketName) {
boolean flag = bucketExists(bucketName);
if (flag) {
return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
}
return null;
}
/**
* 通过文件上传到对象
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @param fileName 文件名(包含文件路径及其后缀)
* @return
*/
@SneakyThrows
public boolean putObject(String bucketName, String objectName, String fileName) {
boolean flag = bucketExists(bucketName);
if (flag) {
UploadObjectArgs uploadArgs = UploadObjectArgs.builder().filename(fileName).bucket(bucketName).object(objectName).build();
ObjectWriteResponse response = minioClient.uploadObject(uploadArgs);
log.info("response:" + JSON.toJSON(response));
StatObjectResponse statObject = statObject(bucketName, objectName);
if (statObject != null && statObject.size() > 0) {
return true;
}
}
return false;
}
/**
* 上传文件
*
* @param multipartFile
* @return
*/
public String putObject(MultipartFile multipartFile) {
return putObject(new MultipartFile[]{multipartFile}).get(0);
}
/**
* 上传文件
*
* @param multipartFiles
* @return
*/
public List<String> putObject(MultipartFile... multipartFiles) {
try {
List<String> retVal = new LinkedList<>();
String[] folders = getDateFolder();
for (MultipartFile multipartFile : multipartFiles) {
// UUID重命名
String fileName = UUID.randomUUID().toString().replace("-", "") + "." + FilenameUtils.getExtension(multipartFile.getOriginalFilename());
// 年/月/日/file
String finalPath = new StringBuilder(String.join(URI_DELIMITER, folders))
.append(URI_DELIMITER)
.append(fileName).toString();
minioClient.putObject(PutObjectArgs.builder()
.stream(multipartFile.getInputStream(), multipartFile.getSize(), -1)
.object(finalPath)
.contentType(multipartFile.getContentType())
.bucket(minioConfig.getBucketName())
.build());
retVal.add(gateway(finalPath));
}
return retVal;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 获取访问网关
*
* @param path
* @return
*/
protected String gateway(String path) {
String gateway = minioConfig.getGateway();
if (!gateway.endsWith(URI_DELIMITER)) {
gateway += URI_DELIMITER;
}
return gateway + path;
}
/**
* 获取年月日[2020, 09, 01]
*
* @return
*/
protected String[] getDateFolder() {
String[] retVal = new String[3];
LocalDate localDate = LocalDate.now();
retVal[0] = localDate.getYear() + "";
int month = localDate.getMonthValue();
retVal[1] = month < 10 ? "0" + month : month + "";
int day = localDate.getDayOfMonth();
retVal[2] = day < 10 ? "0" + day : day + "";
return retVal;
}
/**
* 文件上传
*
* @param bucketName
* @param file
*/
@SneakyThrows
public void putObject(String bucketName, MultipartFile file, String objectName) {
PutObjectArgs args = PutObjectArgs.builder().
bucket(bucketName).object(objectName).
stream(file.getInputStream(), file.getSize(), -1).
contentType(file.getContentType()).build();
minioClient.putObject(args);
}
/**
* 文件对象上传
*
* @param objectName 服务器中对象名称(包含文件后缀)
* @param fileName 文件名(包含文件路径及其后缀)
* @return
*/
public String uploadFileByPath(String objectName, String fileName) {
putObject(minioConfig.getBucketName(), objectName, fileName);
return "http://" + minioConfig.getEndpoint() + ":" +
minioConfig.getPort() + "/" +
minioConfig.getBucketName() + "/" + objectName;
}
/**
* 以流的形式获取一个文件对象
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @return
*/
@SneakyThrows
public InputStream getObject(String bucketName, String objectName) {
boolean flag = bucketExists(bucketName);
if (flag) {
StatObjectResponse statObject = statObject(bucketName, objectName);
if (statObject != null && statObject.size() > 0) {
GetObjectArgs args = GetObjectArgs.builder().bucket(bucketName).object(objectName).build();
InputStream stream = minioClient.getObject(args);
return stream;
}
}
return null;
}
/**
* 以流的形式获取一个文件对象(断点下载)
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @param offset 起始字节的位置
* @param length 要读取的长度 (可选,如果无值则代表读到文件结尾)
* @return
*/
@SneakyThrows
public InputStream getObject(String bucketName, String objectName, long offset, Long length) {
boolean flag = bucketExists(bucketName);
if (flag) {
StatObjectResponse statObject = statObject(bucketName, objectName);
if (statObject != null && statObject.size() > 0) {
GetObjectArgs args = GetObjectArgs.builder().bucket(bucketName).object(objectName).offset(offset).length(length).build();
InputStream stream = minioClient.getObject(args);
return stream;
}
}
return null;
}
/**
* 下载并将文件保存到本地
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @param fileName File name
* @return
*/
@SneakyThrows
public boolean getObject(String bucketName, String objectName, String fileName) {
boolean flag = bucketExists(bucketName);
if (flag) {
StatObjectResponse statObject = statObject(bucketName, objectName);
if (statObject != null && statObject.size() > 0) {
GetObjectArgs args = GetObjectArgs.builder().bucket(bucketName).object(objectName).build();
minioClient.getObject(args);
return true;
}
}
return false;
}
/**
* 删除一个对象
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
*/
@SneakyThrows
public boolean removeObject(String bucketName, String objectName) {
boolean flag = bucketExists(bucketName);
if (flag) {
RemoveObjectArgs args = RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build();
minioClient.removeObject(args);
return true;
}
return false;
}
/**
* 删除指定桶的多个文件对象,返回删除错误的对象列表,全部删除成功,返回空列表
*
* @param bucketName 存储桶名称
* @param objectNames 含有要删除的多个object名称的迭代器对象
* @return
*/
@SneakyThrows
public List<String> removeObject(String bucketName, List<String> objectNames) {
List<String> deleteErrorNames = new ArrayList<>();
boolean flag = bucketExists(bucketName);
if (flag) {
List<DeleteObject> deleteObjects = objectNames.stream().parallel().map(o -> {
DeleteObject deleteObject = new DeleteObject(o);
return deleteObject;
}).collect(Collectors.toList());
RemoveObjectsArgs args = RemoveObjectsArgs.builder().bucket(bucketName).objects(deleteObjects).build();
Iterable<Result<DeleteError>> results = minioClient.removeObjects(args);
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
deleteErrorNames.add(error.objectName());
}
}
return deleteErrorNames;
}
/**
* 生成一个给HTTP GET请求用的presigned URL。
* 浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @param expires 失效时间(以秒为单位),默认是7天,不得大于七天
* @return
*/
@SneakyThrows
public String presignedGetObject(String bucketName, String objectName, Integer expires) {
boolean flag = bucketExists(bucketName);
String url = "";
if (flag) {
if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
throw new McloudHandlerException(expires + ": expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);
}
url = minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder().
method(Method.GET).
bucket(bucketName).
object(objectName).
expiry(expires).
build());
}
return url;
}
/**
* 生成一个给HTTP PUT请求用的presigned URL。
* 浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @param expires 失效时间(以秒为单位),默认是7天,不得大于七天
* @return
*/
@SneakyThrows
public String presignedPutObject(String bucketName, String objectName, Integer expires) {
boolean flag = bucketExists(bucketName);
String url = "";
if (flag) {
if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
throw new McloudHandlerException(expires + ": expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);
}
url = minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.PUT)
.bucket(bucketName)
.object(objectName)
.expiry(expires)
.build());
}
return url;
}
/**
* 获取对象的元数据
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @return
*/
public StatObjectResponse statObject(String bucketName, String objectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException {
boolean flag = bucketExists(bucketName);
if (flag) {
StatObjectArgs objectArgs = StatObjectArgs.builder().bucket(bucketName).object(objectName).build();
StatObjectResponse statObject = minioClient.statObject(objectArgs);
return statObject;
}
return null;
}
/**
* 文件访问路径
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @return
*/
@SneakyThrows
public String getObjectUrl(String bucketName, String objectName) {
boolean flag = bucketExists(bucketName);
String url = "";
if (flag) {
url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).build());
}
return url;
}
/**
* 获取存储桶策略
*
* @param bucketName 存储桶名称
* @return
*/
@SneakyThrows
public String getBucketPolicy(String bucketName) {
return minioClient.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build());
}
/**
* 文件下载
*
* @param bucketName 存储桶名称
* @param objectName 文件名称
* @param originalName 文件原始名
* @param response 响应
*/
public void downloadFile(String bucketName, String objectName, String originalName, HttpServletResponse response) {
try {
GetObjectArgs args = GetObjectArgs.builder().bucket(bucketName).object(objectName).build();
InputStream file = minioClient.getObject(args);
String filename = new String(objectName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);
if (StringUtils.isNotEmpty(originalName)) {
objectName = originalName;
}
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
ServletOutputStream servletOutputStream = response.getOutputStream();
int len;
byte[] buffer = new byte[1024];
while ((len = file.read(buffer)) > 0) {
servletOutputStream.write(buffer, 0, len);
}
servletOutputStream.flush();
file.close();
servletOutputStream.close();
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 文件下载
*
* @param filename
* @param bucketName
* @param objectName
*/
@SneakyThrows
public void downloadFile(String filename, String bucketName, String objectName) {
DownloadObjectArgs args = DownloadObjectArgs.builder().filename(filename).bucket(bucketName).object(objectName).build();
minioClient.downloadObject(args);
}
/**
* 初始化获取分片上传URLS
*
* @param bucketName
* @param objectName
* @param totalPart
* @return
*/
public Map<String, Object> initMultiPartUpload(String bucketName, String objectName, int totalPart) {
Map<String, Object> result = new HashMap<>();
try {
String uploadId = minioClient.initMultiPartUpload(bucketName, null, objectName, null, null);
result.put("uploadId", uploadId);
List<String> partList = new ArrayList<>();
Map<String, String> reqParams = new HashMap<>();
reqParams.put("uploadId", uploadId);
for (int i = 1; i <= totalPart; i++) {
reqParams.put("partNumber", String.valueOf(i));
String uploadUrl = minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.PUT)
.bucket(bucketName)
.object(objectName)
.expiry(1, TimeUnit.DAYS)
.extraQueryParams(reqParams)
.build());
partList.add(uploadUrl);
}
result.put("uploadUrls", partList);
} catch (Exception e) {
log.error("initMultiPartUpload error: {}", e.getMessage(), e);
return null;
}
return result;
}
/**
* 合并分段上传
*
* @param bucketName
* @param objectName
* @param uploadId
* @return
*/
public boolean mergeMultipartUpload(String bucketName, String objectName, String uploadId) {
try {
ListPartsResponse partResult = minioClient.listMultipart(bucketName, null, objectName, 1000, 0, uploadId, null, null);
int partNumber = 1;
Part[] parts = new Part[partResult.result().partList().size()];
for (Part part : partResult.result().partList()) {
parts[partNumber - 1] = new Part(partNumber, part.etag());
partNumber++;
}
minioClient.mergeMultipartUpload(bucketName, null, objectName, uploadId, parts, null, null);
} catch (Exception e) {
log.error("mergeMultipartUpload error: {}", e.getMessage(), e);
return false;
}
return true;
}
}
感谢分享学习
参考:https://blog.csdn.net/Tuine/article/details/113996848