build: 提供默认MinIO存储
This commit is contained in:
parent
3ba24d93a4
commit
c31a9d7ee6
|
@ -1,10 +1,12 @@
|
|||
package io.metersphere;
|
||||
|
||||
import io.metersphere.sdk.config.MinioProperties;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
|
@ -18,6 +20,9 @@ import org.springframework.context.annotation.PropertySource;
|
|||
"file:/opt/metersphere/conf/metersphere.properties",
|
||||
}, encoding = "UTF-8", ignoreResourceNotFound = true)
|
||||
@ServletComponentScan
|
||||
@EnableConfigurationProperties({
|
||||
MinioProperties.class
|
||||
})
|
||||
public class Application {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package io.metersphere.sdk.config;
|
||||
|
||||
import io.minio.BucketExistsArgs;
|
||||
import io.minio.MakeBucketArgs;
|
||||
import io.minio.MinioClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class MinioConfig {
|
||||
|
||||
public static final String BUCKET = "metersphere";
|
||||
|
||||
@Bean
|
||||
public MinioClient minioClient(MinioProperties minioProperties) throws Exception {
|
||||
// 创建 MinioClient 客户端
|
||||
MinioClient minioClient = MinioClient.builder()
|
||||
.endpoint(minioProperties.getEndpoint())
|
||||
.credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
|
||||
.build();
|
||||
|
||||
boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET).build());
|
||||
if (!exist) {
|
||||
minioClient.makeBucket(MakeBucketArgs.builder().bucket(BUCKET).build());
|
||||
}
|
||||
return minioClient;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package io.metersphere.sdk.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = MinioProperties.MINIO_PREFIX)
|
||||
@Getter
|
||||
@Setter
|
||||
public class MinioProperties {
|
||||
public static final String MINIO_PREFIX = "minio";
|
||||
|
||||
private String endpoint;
|
||||
private String accessKey;
|
||||
private String secretKey;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package io.metersphere.sdk.constants;
|
||||
|
||||
public enum StorageConstants {
|
||||
MINIO, GIT
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package io.metersphere.sdk.file;
|
||||
|
||||
import io.metersphere.sdk.util.CommonBeanFactory;
|
||||
|
||||
public class FileCenter {
|
||||
|
||||
// 多种实现时打开
|
||||
/*public static FileRepository getRepository(String storage) {
|
||||
if (StringUtils.equals(StorageConstants.GIT.name(), storage)) {
|
||||
LogUtils.info("扩展GIT存储方式");
|
||||
return null;
|
||||
} else {
|
||||
return getDefaultRepository();
|
||||
}
|
||||
}*/
|
||||
|
||||
public static FileRepository getDefaultRepository() {
|
||||
return CommonBeanFactory.getBean(MinioRepository.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package io.metersphere.sdk.file;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
public interface FileRepository {
|
||||
/**
|
||||
* 保存文件
|
||||
*
|
||||
* @param file
|
||||
* @param request
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public String saveFile(MultipartFile file, FileRequest request) throws Exception;
|
||||
|
||||
/**
|
||||
* 保存文件
|
||||
*
|
||||
* @param bytes
|
||||
* @param request
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public String saveFile(byte[] bytes, FileRequest request) throws Exception;
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param request
|
||||
* @throws Exception
|
||||
*/
|
||||
public void delete(FileRequest request) throws Exception;
|
||||
|
||||
/**
|
||||
* 获取文件字节内容
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public byte[] getFile(FileRequest request) throws Exception;
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package io.metersphere.sdk.file;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FileRequest {
|
||||
// 项目id
|
||||
private String projectId;
|
||||
|
||||
// 存储类型
|
||||
private String storage;
|
||||
|
||||
// 资源id为空时存储在项目目录下
|
||||
private String resourceId;
|
||||
|
||||
// 文件名称
|
||||
private String fileName;
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package io.metersphere.sdk.file;
|
||||
|
||||
import io.metersphere.sdk.config.MinioConfig;
|
||||
import io.minio.*;
|
||||
import io.minio.messages.Item;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class MinioRepository implements FileRepository {
|
||||
|
||||
@Resource
|
||||
private MinioClient client;
|
||||
|
||||
private String getPath(FileRequest request) {
|
||||
// 文件存储路径
|
||||
return StringUtils.join(
|
||||
request.getProjectId(),
|
||||
File.separator,
|
||||
StringUtils.isNotBlank(request.getResourceId()) ? request.getResourceId() + File.separator : StringUtils.EMPTY,
|
||||
request.getFileName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveFile(MultipartFile file, FileRequest request) throws Exception {
|
||||
// 文件存储路径
|
||||
String filePath = getPath(request);
|
||||
client.putObject(PutObjectArgs.builder()
|
||||
.bucket(MinioConfig.BUCKET)
|
||||
.object(filePath)
|
||||
.stream(file.getInputStream(), file.getSize(), -1) // 文件内容
|
||||
.build());
|
||||
return request.getFileName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveFile(byte[] bytes, FileRequest request) throws Exception {
|
||||
String filePath = getPath(request);
|
||||
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
|
||||
client.putObject(PutObjectArgs.builder()
|
||||
.bucket(MinioConfig.BUCKET)
|
||||
.object(filePath)
|
||||
.stream(inputStream, bytes.length, -1)
|
||||
.build());
|
||||
}
|
||||
return request.getFileName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(FileRequest request) throws Exception {
|
||||
String filePath = getPath(request);
|
||||
// 删除单个文件
|
||||
removeObject(MinioConfig.BUCKET, filePath);
|
||||
}
|
||||
|
||||
|
||||
private boolean removeObject(String bucketName, String objectName) throws Exception {
|
||||
client.removeObject(RemoveObjectArgs.builder()
|
||||
.bucket(bucketName) // 存储桶
|
||||
.object(objectName) // 文件名
|
||||
.build());
|
||||
return true;
|
||||
}
|
||||
|
||||
public void removeObjects(String bucketName, String objectName) throws Exception {
|
||||
List<String> objects = listObjects(bucketName, objectName);
|
||||
for (String object : objects) {
|
||||
removeObject(bucketName, object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归获取某路径下的所有文件
|
||||
*/
|
||||
public List<String> listObjects(String bucketName, String objectName) throws Exception {
|
||||
List<String> list = new ArrayList<>(12);
|
||||
Iterable<Result<Item>> results = client.listObjects(
|
||||
ListObjectsArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.prefix(objectName)
|
||||
.build());
|
||||
for (Result<Item> result : results) {
|
||||
Item item = result.get();
|
||||
if (item.isDir()) {
|
||||
List<String> files = listObjects(bucketName, item.objectName());
|
||||
list.addAll(files);
|
||||
} else {
|
||||
list.add(item.objectName());
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getFile(FileRequest request) throws Exception {
|
||||
return getFileAsStream(request).readAllBytes();
|
||||
}
|
||||
|
||||
public InputStream getFileAsStream(FileRequest request) throws Exception {
|
||||
String fileName = getPath(request);
|
||||
return client.getObject(GetObjectArgs.builder()
|
||||
.bucket(MinioConfig.BUCKET) // 存储桶
|
||||
.object(fileName) // 文件名
|
||||
.build());
|
||||
}
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
package io.metersphere.api;
|
||||
|
||||
import io.metersphere.sdk.config.MinioProperties;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
|
@ -13,6 +15,9 @@ import org.springframework.context.annotation.ComponentScan;
|
|||
LdapAutoConfiguration.class,
|
||||
Neo4jAutoConfiguration.class
|
||||
})
|
||||
@EnableConfigurationProperties({
|
||||
MinioProperties.class
|
||||
})
|
||||
@ServletComponentScan
|
||||
@ComponentScan(basePackages = {"io.metersphere.sdk", "io.metersphere.api"})
|
||||
public class Application {
|
||||
|
|
|
@ -10,4 +10,4 @@ embedded.redis.enabled=true
|
|||
# kafka
|
||||
embedded.kafka.enabled=false
|
||||
# minio
|
||||
embedded.minio.enabled=false
|
||||
embedded.minio.enabled=true
|
|
@ -1,10 +1,12 @@
|
|||
package io.metersphere.bug;
|
||||
|
||||
import io.metersphere.sdk.config.MinioProperties;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
|
@ -13,6 +15,9 @@ import org.springframework.context.annotation.ComponentScan;
|
|||
LdapAutoConfiguration.class,
|
||||
Neo4jAutoConfiguration.class
|
||||
})
|
||||
@EnableConfigurationProperties({
|
||||
MinioProperties.class
|
||||
})
|
||||
@ServletComponentScan
|
||||
@ComponentScan(basePackages = {"io.metersphere.sdk", "io.metersphere.bug"})
|
||||
public class Application {
|
||||
|
|
|
@ -10,4 +10,4 @@ embedded.redis.enabled=true
|
|||
# kafka
|
||||
embedded.kafka.enabled=false
|
||||
# minio
|
||||
embedded.minio.enabled=false
|
||||
embedded.minio.enabled=true
|
|
@ -1,11 +1,13 @@
|
|||
package io.metersphere.functional;
|
||||
|
||||
|
||||
import io.metersphere.sdk.config.MinioProperties;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
|
@ -14,6 +16,9 @@ import org.springframework.context.annotation.ComponentScan;
|
|||
LdapAutoConfiguration.class,
|
||||
Neo4jAutoConfiguration.class
|
||||
})
|
||||
@EnableConfigurationProperties({
|
||||
MinioProperties.class
|
||||
})
|
||||
@ServletComponentScan
|
||||
@ComponentScan(basePackages = {"io.metersphere.sdk", "io.metersphere.functional"})
|
||||
public class Application {
|
||||
|
|
|
@ -10,4 +10,4 @@ embedded.redis.enabled=true
|
|||
# kafka
|
||||
embedded.kafka.enabled=false
|
||||
# minio
|
||||
embedded.minio.enabled=false
|
||||
embedded.minio.enabled=true
|
|
@ -1,10 +1,12 @@
|
|||
package io.metersphere.project;
|
||||
|
||||
import io.metersphere.sdk.config.MinioProperties;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
|
@ -13,6 +15,9 @@ import org.springframework.context.annotation.ComponentScan;
|
|||
LdapAutoConfiguration.class,
|
||||
Neo4jAutoConfiguration.class
|
||||
})
|
||||
@EnableConfigurationProperties({
|
||||
MinioProperties.class
|
||||
})
|
||||
@ServletComponentScan
|
||||
@ComponentScan(basePackages = {"io.metersphere.sdk", "io.metersphere.project"})
|
||||
public class Application {
|
||||
|
|
|
@ -10,4 +10,4 @@ embedded.redis.enabled=true
|
|||
# kafka
|
||||
embedded.kafka.enabled=false
|
||||
# minio
|
||||
embedded.minio.enabled=false
|
||||
embedded.minio.enabled=true
|
|
@ -1,10 +1,12 @@
|
|||
package io.metersphere.system;
|
||||
|
||||
import io.metersphere.sdk.config.MinioProperties;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
|
@ -13,6 +15,9 @@ import org.springframework.context.annotation.ComponentScan;
|
|||
LdapAutoConfiguration.class,
|
||||
Neo4jAutoConfiguration.class
|
||||
})
|
||||
@EnableConfigurationProperties({
|
||||
MinioProperties.class
|
||||
})
|
||||
@ServletComponentScan
|
||||
@ComponentScan(basePackages = {"io.metersphere.sdk", "io.metersphere.system"})
|
||||
public class Application {
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
package io.metersphere.system.controller;
|
||||
|
||||
import io.metersphere.sdk.file.FileCenter;
|
||||
import io.metersphere.sdk.file.FileRepository;
|
||||
import io.metersphere.sdk.file.FileRequest;
|
||||
import io.metersphere.sdk.file.MinioRepository;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class FileCenterTest {
|
||||
|
||||
@Resource
|
||||
private MinioRepository repository;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testRepository() throws Exception {
|
||||
FileRepository repository = FileCenter.getDefaultRepository();
|
||||
Assertions.assertTrue(repository instanceof MinioRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testSaveFile() throws Exception {
|
||||
MockMultipartFile mockFile = new MockMultipartFile(
|
||||
"file",
|
||||
"test.txt",
|
||||
"text/plain",
|
||||
"Hello, World!".getBytes()
|
||||
);
|
||||
// 创建一个FileRequest对象作为测试用的请求参数
|
||||
FileRequest request = new FileRequest();
|
||||
request.setFileName("test.txt");
|
||||
request.setProjectId("test-project");
|
||||
request.setResourceId("test-resource-id");
|
||||
repository.saveFile(mockFile, request);
|
||||
Assertions.assertTrue(repository.saveFile(mockFile, request) != null);
|
||||
Assertions.assertTrue(repository.saveFile("Hello, World!".getBytes(), request) != null);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testGetFile() throws Exception {
|
||||
// 创建一个FileRequest对象作为测试用的请求参数
|
||||
FileRequest request = new FileRequest();
|
||||
request.setFileName("test.txt");
|
||||
request.setProjectId("test-project");
|
||||
request.setResourceId("test-resource-id");
|
||||
repository.getFile(request);
|
||||
Assertions.assertTrue(repository.getFile(request) != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testDelFile() throws Exception {
|
||||
// 创建一个FileRequest对象作为测试用的请求参数
|
||||
FileRequest request = new FileRequest();
|
||||
request.setFileName("test.txt");
|
||||
request.setProjectId("test-project");
|
||||
request.setResourceId("test-resource-id");
|
||||
repository.delete(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void testFile() throws Exception {
|
||||
MockMultipartFile mockFile = new MockMultipartFile(
|
||||
"file",
|
||||
"test.txt",
|
||||
"text/plain",
|
||||
"Hello, World!".getBytes()
|
||||
);
|
||||
// 创建一个FileRequest对象作为测试用的请求参数
|
||||
FileRequest request = new FileRequest();
|
||||
request.setFileName("test.txt");
|
||||
request.setProjectId("test-project");
|
||||
repository.saveFile(mockFile, request);
|
||||
Assertions.assertTrue(repository.saveFile(mockFile, request) != null);
|
||||
Assertions.assertTrue(repository.saveFile("Hello, World!".getBytes(), request) != null);
|
||||
|
||||
}
|
||||
}
|
|
@ -10,4 +10,4 @@ embedded.redis.enabled=true
|
|||
# kafka
|
||||
embedded.kafka.enabled=false
|
||||
# minio
|
||||
embedded.minio.enabled=false
|
||||
embedded.minio.enabled=true
|
|
@ -1,10 +1,12 @@
|
|||
package io.metersphere.plan;
|
||||
|
||||
import io.metersphere.sdk.config.MinioProperties;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
|
@ -13,6 +15,9 @@ import org.springframework.context.annotation.ComponentScan;
|
|||
LdapAutoConfiguration.class,
|
||||
Neo4jAutoConfiguration.class
|
||||
})
|
||||
@EnableConfigurationProperties({
|
||||
MinioProperties.class
|
||||
})
|
||||
@ServletComponentScan
|
||||
@ComponentScan(basePackages = {"io.metersphere.sdk", "io.metersphere.plan"})
|
||||
public class Application {
|
||||
|
|
|
@ -10,4 +10,4 @@ embedded.redis.enabled=true
|
|||
# kafka
|
||||
embedded.kafka.enabled=false
|
||||
# minio
|
||||
embedded.minio.enabled=false
|
||||
embedded.minio.enabled=true
|
Loading…
Reference in New Issue