feat:
This commit is contained in:
parent
ba3c78baeb
commit
5e5a51425c
14
pom.xml
14
pom.xml
|
@ -160,6 +160,20 @@
|
||||||
<artifactId>aspectjweaver</artifactId>
|
<artifactId>aspectjweaver</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MinIO -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.minio</groupId>
|
||||||
|
<artifactId>minio</artifactId>
|
||||||
|
<version>8.5.7</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 文件处理 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>2.11.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.guwan.backend.config;
|
||||||
|
|
||||||
|
import io.minio.MinioClient;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "minio")
|
||||||
|
public class MinioConfig {
|
||||||
|
|
||||||
|
private String endpoint;
|
||||||
|
private String accessKey;
|
||||||
|
private String secretKey;
|
||||||
|
private Bucket bucket;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Bucket {
|
||||||
|
private String files;
|
||||||
|
private String images;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MinioClient minioClient() {
|
||||||
|
return MinioClient.builder()
|
||||||
|
.endpoint(endpoint)
|
||||||
|
.credentials(accessKey, secretKey)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
package com.guwan.backend.util;
|
||||||
|
|
||||||
|
import io.minio.*;
|
||||||
|
import io.minio.http.Method;
|
||||||
|
import io.minio.messages.Bucket;
|
||||||
|
import io.minio.messages.DeleteObject;
|
||||||
|
import io.minio.messages.Item;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MinioUtil {
|
||||||
|
|
||||||
|
private final MinioClient minioClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建存储桶
|
||||||
|
*/
|
||||||
|
public void createBucket(String bucketName) {
|
||||||
|
try {
|
||||||
|
boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
|
||||||
|
if (!found) {
|
||||||
|
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("创建存储桶失败", e);
|
||||||
|
throw new RuntimeException("创建存储桶失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件
|
||||||
|
*/
|
||||||
|
public String uploadFile(String bucketName, MultipartFile file) {
|
||||||
|
try {
|
||||||
|
String fileName = generateFileName(file.getOriginalFilename());
|
||||||
|
InputStream inputStream = file.getInputStream();
|
||||||
|
minioClient.putObject(
|
||||||
|
PutObjectArgs.builder()
|
||||||
|
.bucket(bucketName)
|
||||||
|
.object(fileName)
|
||||||
|
.stream(inputStream, file.getSize(), -1)
|
||||||
|
.contentType(file.getContentType())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
return fileName;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("上传文件失败", e);
|
||||||
|
throw new RuntimeException("上传文件失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传Base64图片
|
||||||
|
*/
|
||||||
|
public String uploadBase64Image(String bucketName, String base64Image, String folder) {
|
||||||
|
try {
|
||||||
|
String[] parts = base64Image.split(",");
|
||||||
|
String imageData = parts[1];
|
||||||
|
byte[] bytes = Base64.getDecoder().decode(imageData);
|
||||||
|
|
||||||
|
String fileName = folder + "/" + UUID.randomUUID() + ".jpg";
|
||||||
|
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
|
||||||
|
|
||||||
|
minioClient.putObject(
|
||||||
|
PutObjectArgs.builder()
|
||||||
|
.bucket(bucketName)
|
||||||
|
.object(fileName)
|
||||||
|
.stream(inputStream, bytes.length, -1)
|
||||||
|
.contentType("image/jpeg")
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
return fileName;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("上传Base64图片失败", e);
|
||||||
|
throw new RuntimeException("上传Base64图片失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文件
|
||||||
|
*/
|
||||||
|
public void deleteFile(String bucketName, String fileName) {
|
||||||
|
try {
|
||||||
|
minioClient.removeObject(
|
||||||
|
RemoveObjectArgs.builder()
|
||||||
|
.bucket(bucketName)
|
||||||
|
.object(fileName)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除文件失败", e);
|
||||||
|
throw new RuntimeException("删除文件失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除文件
|
||||||
|
*/
|
||||||
|
public void deleteFiles(String bucketName, List<String> fileNames) {
|
||||||
|
try {
|
||||||
|
List<DeleteObject> objects = fileNames.stream()
|
||||||
|
.map(DeleteObject::new)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
Iterable<Result<DeleteError>> results = minioClient.removeObjects(
|
||||||
|
RemoveObjectsArgs.builder()
|
||||||
|
.bucket(bucketName)
|
||||||
|
.objects(objects)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
for (Result<DeleteError> result : results) {
|
||||||
|
DeleteError error = result.get();
|
||||||
|
log.error("删除文件失败: {}", error.message());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("批量删除文件失败", e);
|
||||||
|
throw new RuntimeException("批量删除文件失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件访问URL
|
||||||
|
*/
|
||||||
|
public String getFileUrl(String bucketName, String fileName) {
|
||||||
|
try {
|
||||||
|
return minioClient.getPresignedObjectUrl(
|
||||||
|
GetPresignedObjectUrlArgs.builder()
|
||||||
|
.method(Method.GET)
|
||||||
|
.bucket(bucketName)
|
||||||
|
.object(fileName)
|
||||||
|
.expiry(7, TimeUnit.DAYS)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取文件访问URL失败", e);
|
||||||
|
throw new RuntimeException("获取文件访问URL失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成文件名
|
||||||
|
*/
|
||||||
|
private String generateFileName(String originalFilename) {
|
||||||
|
String extension = FilenameUtils.getExtension(originalFilename);
|
||||||
|
return UUID.randomUUID() + "." + extension;
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,9 +74,14 @@ jwt:
|
||||||
aliyun:
|
aliyun:
|
||||||
sms:
|
sms:
|
||||||
|
|
||||||
|
# MinIO配置
|
||||||
|
minio:
|
||||||
|
endpoint: http://localhost:9000
|
||||||
|
accessKey: minioadmin
|
||||||
|
secretKey: minioadmin
|
||||||
|
bucket:
|
||||||
|
files: files # 文件桶
|
||||||
|
images: images # 图片桶
|
||||||
|
|
||||||
# 文件上传配置
|
# 文件上传配置
|
||||||
file:
|
file:
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
package com.guwan.backend.util;
|
||||||
|
|
||||||
|
import com.guwan.backend.config.MinioConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class MinioUtilTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MinioUtil minioUtil;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MinioConfig minioConfig;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUploadAndDeleteFile() {
|
||||||
|
// 创建测试文件
|
||||||
|
String content = "test content";
|
||||||
|
MockMultipartFile file = new MockMultipartFile(
|
||||||
|
"test.txt",
|
||||||
|
"test.txt",
|
||||||
|
"text/plain",
|
||||||
|
content.getBytes(StandardCharsets.UTF_8)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
String fileName = minioUtil.uploadFile(minioConfig.getBucket().getFiles(), file);
|
||||||
|
assertNotNull(fileName);
|
||||||
|
|
||||||
|
// 获取文件URL
|
||||||
|
String url = minioUtil.getFileUrl(minioConfig.getBucket().getFiles(), fileName);
|
||||||
|
assertNotNull(url);
|
||||||
|
assertTrue(url.contains(fileName));
|
||||||
|
|
||||||
|
// 删除文件
|
||||||
|
minioUtil.deleteFile(minioConfig.getBucket().getFiles(), fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUploadBase64Image() {
|
||||||
|
// 创建Base64图片
|
||||||
|
String base64Image = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=";
|
||||||
|
|
||||||
|
// 上传图片
|
||||||
|
String fileName = minioUtil.uploadBase64Image(
|
||||||
|
minioConfig.getBucket().getImages(),
|
||||||
|
base64Image,
|
||||||
|
"test"
|
||||||
|
);
|
||||||
|
assertNotNull(fileName);
|
||||||
|
|
||||||
|
// 获取图片URL
|
||||||
|
String url = minioUtil.getFileUrl(minioConfig.getBucket().getImages(), fileName);
|
||||||
|
assertNotNull(url);
|
||||||
|
assertTrue(url.contains(fileName));
|
||||||
|
|
||||||
|
// 删除图片
|
||||||
|
minioUtil.deleteFile(minioConfig.getBucket().getImages(), fileName);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue