mirror of
https://gitee.com/lxp135/minio-plus.git
synced 2025-12-06 08:58:25 +08:00
initial spring-boot 3 supports
This commit is contained in:
parent
6e84322c79
commit
91f2a84105
7
.gitignore
vendored
7
.gitignore
vendored
@ -16,3 +16,10 @@ buildNumber.properties
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
/docs/.vitepress/cache/
|
||||
|
||||
# IDEA
|
||||
.idea/
|
||||
|
||||
# logs
|
||||
**/logs
|
||||
**/*.log
|
||||
@ -34,6 +34,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
|
||||
@ -4,18 +4,20 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* SpringMVC配置
|
||||
*
|
||||
* @author contact@liuxp.me
|
||||
* @since 2024/06/11
|
||||
*/
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
@Resource
|
||||
private LoginUserInterceptor loginUserInterceptor;
|
||||
private final LoginUserInterceptor loginUserInterceptor;
|
||||
|
||||
public WebMvcConfig(LoginUserInterceptor loginUserInterceptor) {
|
||||
this.loginUserInterceptor = loginUserInterceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 前置拦截器
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
|
||||
@ -7,11 +7,11 @@ import org.liuxp.minioplus.api.model.vo.FileMetadataInfoVo;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 定时任务,演示工程定期清理文件
|
||||
*
|
||||
* @author contact@liuxp.me
|
||||
* @since 2024/06/17
|
||||
*/
|
||||
@ -19,20 +19,23 @@ import java.util.List;
|
||||
@Slf4j
|
||||
public class CleanTask {
|
||||
|
||||
@Resource
|
||||
private StorageService storageService;
|
||||
private final StorageService storageService;
|
||||
|
||||
public CleanTask(StorageService storageService) {
|
||||
this.storageService = storageService;
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 */1 * * ?")
|
||||
public void clean(){
|
||||
public void clean() {
|
||||
|
||||
List<FileMetadataInfoVo> fileList = storageService.list(new FileMetadataInfoDTO());
|
||||
|
||||
for (FileMetadataInfoVo infoVo : fileList) {
|
||||
log.info("文件清理,文件key="+infoVo.getFileKey());
|
||||
try{
|
||||
log.info("文件清理,文件key=" + infoVo.getFileKey());
|
||||
try {
|
||||
storageService.remove(infoVo.getFileKey());
|
||||
}catch (Exception e){
|
||||
log.error(infoVo.getFileKey()+"文件清理失败",e);
|
||||
} catch (Exception e) {
|
||||
log.error(infoVo.getFileKey() + "文件清理失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,23 +16,12 @@
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<springboot3.version>3.3.2</springboot3.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>me.liuxp</groupId>
|
||||
<artifactId>minio-plus-all-springboot3-starter</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.liuxp</groupId>
|
||||
@ -46,35 +35,45 @@
|
||||
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<version>${springboot3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${springboot3.version}</version>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<version>${mybatisplus.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
<version>${springboot3.version}</version>
|
||||
<version>${spring-boot3.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- spring-boot-dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot3.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${springboot3.version}</version>
|
||||
<version>${spring-boot3.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
|
||||
@ -8,8 +8,8 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 登录用户拦截器
|
||||
|
||||
@ -4,18 +4,21 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* SpringMVC配置
|
||||
*
|
||||
* @author contact@liuxp.me
|
||||
* @since 2024/06/11
|
||||
*/
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
@Resource
|
||||
private LoginUserInterceptor loginUserInterceptor;
|
||||
private final LoginUserInterceptor loginUserInterceptor;
|
||||
|
||||
public WebMvcConfig(LoginUserInterceptor loginUserInterceptor) {
|
||||
this.loginUserInterceptor = loginUserInterceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 前置拦截器
|
||||
|
||||
@ -24,11 +24,15 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- 自定义的配置类生成元数据信息 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@ -1,16 +1,10 @@
|
||||
package org.liuxp.minioplus.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* MinioPlusProperties自动配置类
|
||||
* MinioPlus自动配置类
|
||||
*
|
||||
* @author contact@liuxp.me
|
||||
*/
|
||||
public class MinioPlusConfig {
|
||||
|
||||
@Bean
|
||||
public MinioPlusProperties tosProperties() {
|
||||
return new MinioPlusProperties();
|
||||
}
|
||||
|
||||
// do noting cuz use components scan
|
||||
}
|
||||
@ -5,6 +5,7 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* MinioPlus配置类
|
||||
@ -14,6 +15,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "minioplus")
|
||||
public class MinioPlusProperties {
|
||||
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.liuxp.minioplus.common.config.MinioPlusConfig
|
||||
@ -0,0 +1 @@
|
||||
org.liuxp.minioplus.common.config.MinioPlusConfig
|
||||
@ -19,6 +19,12 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.liuxp</groupId>
|
||||
<artifactId>minio-plus-common</artifactId>
|
||||
|
||||
@ -26,7 +26,6 @@ import org.liuxp.minioplus.s3.def.ListParts;
|
||||
import org.liuxp.minioplus.s3.def.MinioS3Client;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
@ -44,14 +43,17 @@ import java.util.Optional;
|
||||
@Slf4j
|
||||
public class StorageEngineServiceImpl implements StorageEngineService {
|
||||
|
||||
@Resource
|
||||
MetadataRepository metadataRepository;
|
||||
private final MetadataRepository metadataRepository;
|
||||
|
||||
@Resource
|
||||
MinioPlusProperties properties;
|
||||
private final MinioPlusProperties properties;
|
||||
|
||||
@Resource
|
||||
MinioS3Client minioS3Client;
|
||||
private final MinioS3Client minioS3Client;
|
||||
|
||||
public StorageEngineServiceImpl(MetadataRepository metadataRepository, MinioPlusProperties properties, MinioS3Client minioS3Client) {
|
||||
this.metadataRepository = metadataRepository;
|
||||
this.properties = properties;
|
||||
this.minioS3Client = minioS3Client;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传任务初始化
|
||||
@ -365,7 +367,7 @@ public class StorageEngineServiceImpl implements StorageEngineService {
|
||||
// 文件权限校验,元数据为空或者当前登录用户不是文件所有者时抛出异常
|
||||
this.authentication(metadata, fileKey, userId);
|
||||
|
||||
if(!StorageBucketEnums.IMAGE.getCode().equals(metadata.getStorageBucket())){
|
||||
if (!StorageBucketEnums.IMAGE.getCode().equals(metadata.getStorageBucket())) {
|
||||
// 不是图片时,返回文件类型
|
||||
return metadata.getFileSuffix();
|
||||
}
|
||||
@ -413,7 +415,7 @@ public class StorageEngineServiceImpl implements StorageEngineService {
|
||||
@Override
|
||||
public Boolean createFile(FileMetadataInfoSaveDTO saveDTO, byte[] fileBytes) {
|
||||
// 写入文件
|
||||
return createFile(saveDTO,new ByteArrayInputStream(fileBytes));
|
||||
return createFile(saveDTO, new ByteArrayInputStream(fileBytes));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -538,6 +540,7 @@ public class StorageEngineServiceImpl implements StorageEngineService {
|
||||
* @param fileSize 文件大小
|
||||
* @return {@link Integer}
|
||||
*/
|
||||
@Override
|
||||
public Integer computeChunkNum(Long fileSize) {
|
||||
// 计算分块数量
|
||||
double tempNum = (double) fileSize / properties.getPart().getSize();
|
||||
|
||||
@ -26,13 +26,13 @@ import org.liuxp.minioplus.core.engine.StorageEngineService;
|
||||
import org.liuxp.minioplus.core.repository.MetadataRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 存储组件Service层公共方法实现类
|
||||
*
|
||||
* @author contact@liuxp.me
|
||||
* @since 2023/06/26
|
||||
*/
|
||||
@ -42,20 +42,23 @@ public class StorageServiceImpl implements StorageService {
|
||||
/**
|
||||
* 存储引擎Service接口定义
|
||||
*/
|
||||
@Resource
|
||||
StorageEngineService storageEngineService;
|
||||
private final StorageEngineService storageEngineService;
|
||||
|
||||
/**
|
||||
* 文件元数据服务接口定义
|
||||
*/
|
||||
@Resource
|
||||
MetadataRepository fileMetadataRepository;
|
||||
private final MetadataRepository fileMetadataRepository;
|
||||
|
||||
/**
|
||||
* MinioPlus配置信息注入类
|
||||
*/
|
||||
@Resource
|
||||
MinioPlusProperties properties;
|
||||
private final MinioPlusProperties properties;
|
||||
|
||||
public StorageServiceImpl(StorageEngineService storageEngineService, MetadataRepository fileMetadataRepository, MinioPlusProperties properties) {
|
||||
this.storageEngineService = storageEngineService;
|
||||
this.fileMetadataRepository = fileMetadataRepository;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePreShardingVo sharding(long fileSize) {
|
||||
@ -94,11 +97,11 @@ public class StorageServiceImpl implements StorageService {
|
||||
public FileCheckResultVo init(String fileMd5, String fullFileName, long fileSize, Boolean isPrivate, String userId) {
|
||||
|
||||
// isPrivate 为空时,设置为 false
|
||||
isPrivate = isPrivate!=null && isPrivate ;
|
||||
isPrivate = isPrivate != null && isPrivate;
|
||||
|
||||
FileCheckResultVo resultVo = storageEngineService.init(fileMd5,fullFileName,fileSize,isPrivate,userId);
|
||||
FileCheckResultVo resultVo = storageEngineService.init(fileMd5, fullFileName, fileSize, isPrivate, userId);
|
||||
|
||||
if(resultVo!=null){
|
||||
if (resultVo != null) {
|
||||
for (FileCheckResultVo.Part part : resultVo.getPartList()) {
|
||||
part.setUrl(remakeUrl(part.getUrl()));
|
||||
}
|
||||
@ -109,9 +112,9 @@ public class StorageServiceImpl implements StorageService {
|
||||
|
||||
@Override
|
||||
public CompleteResultVo complete(String fileKey, List<String> partMd5List, String userId) {
|
||||
CompleteResultVo completeResultVo = storageEngineService.complete(fileKey,partMd5List,userId);
|
||||
CompleteResultVo completeResultVo = storageEngineService.complete(fileKey, partMd5List, userId);
|
||||
|
||||
if(completeResultVo!=null){
|
||||
if (completeResultVo != null) {
|
||||
for (FileCheckResultVo.Part part : completeResultVo.getPartList()) {
|
||||
part.setUrl(remakeUrl(part.getUrl()));
|
||||
}
|
||||
@ -122,17 +125,17 @@ public class StorageServiceImpl implements StorageService {
|
||||
|
||||
@Override
|
||||
public String download(String fileKey, String userId) {
|
||||
return storageEngineService.download(fileKey,userId);
|
||||
return storageEngineService.download(fileKey, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String image(String fileKey, String userId) {
|
||||
return storageEngineService.image(fileKey,userId);
|
||||
return storageEngineService.image(fileKey, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String preview(String fileKey, String userId) {
|
||||
return storageEngineService.preview(fileKey,userId);
|
||||
return storageEngineService.preview(fileKey, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -154,7 +157,7 @@ public class StorageServiceImpl implements StorageService {
|
||||
public FileMetadataInfoVo createFile(String fullFileName, Boolean isPrivate, String userId, byte[] fileBytes) {
|
||||
|
||||
// 组装文件保存入参
|
||||
FileMetadataInfoSaveDTO saveDTO = buildSaveDto(fullFileName, isPrivate, userId,fileBytes);
|
||||
FileMetadataInfoSaveDTO saveDTO = buildSaveDto(fullFileName, isPrivate, userId, fileBytes);
|
||||
|
||||
// 查询MinIO中是否存在相同MD5值的文件
|
||||
FileMetadataInfoDTO fileMetadataInfo = new FileMetadataInfoDTO();
|
||||
@ -163,9 +166,9 @@ public class StorageServiceImpl implements StorageService {
|
||||
|
||||
boolean sameMd5 = false;
|
||||
|
||||
if(CollUtil.isNotEmpty(alreadyFileList)){
|
||||
if (CollUtil.isNotEmpty(alreadyFileList)) {
|
||||
for (FileMetadataInfoVo fileMetadataInfoVo : alreadyFileList) {
|
||||
if(Boolean.TRUE.equals(fileMetadataInfoVo.getIsFinished())){
|
||||
if (Boolean.TRUE.equals(fileMetadataInfoVo.getIsFinished())) {
|
||||
saveDTO.setStorageBucket(fileMetadataInfoVo.getStorageBucket());
|
||||
saveDTO.setStoragePath(fileMetadataInfoVo.getStoragePath());
|
||||
sameMd5 = true;
|
||||
@ -174,7 +177,7 @@ public class StorageServiceImpl implements StorageService {
|
||||
}
|
||||
}
|
||||
|
||||
if(!sameMd5){
|
||||
if (!sameMd5) {
|
||||
// 新文件时,执行写入逻辑
|
||||
storageEngineService.createFile(saveDTO, fileBytes);
|
||||
}
|
||||
@ -185,7 +188,7 @@ public class StorageServiceImpl implements StorageService {
|
||||
|
||||
@Override
|
||||
public FileMetadataInfoVo createFile(String fullFileName, Boolean isPrivate, String userId, InputStream inputStream) {
|
||||
return createFile(fullFileName, isPrivate, userId,IoUtil.readBytes(inputStream));
|
||||
return createFile(fullFileName, isPrivate, userId, IoUtil.readBytes(inputStream));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -195,13 +198,13 @@ public class StorageServiceImpl implements StorageService {
|
||||
// 获得输入流
|
||||
InputStream inputStream = httpResponse.bodyStream();
|
||||
// 调用处理函数
|
||||
return createFile(fullFileName, isPrivate, userId,inputStream);
|
||||
return createFile(fullFileName, isPrivate, userId, inputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileMetadataInfoVo createBigFile(String fullFileName, String md5, long fileSize,Boolean isPrivate, String userId,InputStream inputStream) {
|
||||
public FileMetadataInfoVo createBigFile(String fullFileName, String md5, long fileSize, Boolean isPrivate, String userId, InputStream inputStream) {
|
||||
// 组装文件保存入参
|
||||
FileMetadataInfoSaveDTO saveDTO = buildSaveDto(fullFileName,md5,fileSize, isPrivate, userId);
|
||||
FileMetadataInfoSaveDTO saveDTO = buildSaveDto(fullFileName, md5, fileSize, isPrivate, userId);
|
||||
|
||||
// 查询MinIO中是否存在相同MD5值的文件
|
||||
FileMetadataInfoDTO fileMetadataInfo = new FileMetadataInfoDTO();
|
||||
@ -210,9 +213,9 @@ public class StorageServiceImpl implements StorageService {
|
||||
|
||||
boolean sameMd5 = false;
|
||||
|
||||
if(CollUtil.isNotEmpty(alreadyFileList)){
|
||||
if (CollUtil.isNotEmpty(alreadyFileList)) {
|
||||
for (FileMetadataInfoVo fileMetadataInfoVo : alreadyFileList) {
|
||||
if(Boolean.TRUE.equals(fileMetadataInfoVo.getIsFinished())){
|
||||
if (Boolean.TRUE.equals(fileMetadataInfoVo.getIsFinished())) {
|
||||
saveDTO.setStorageBucket(fileMetadataInfoVo.getStorageBucket());
|
||||
saveDTO.setStoragePath(fileMetadataInfoVo.getStoragePath());
|
||||
sameMd5 = true;
|
||||
@ -221,7 +224,7 @@ public class StorageServiceImpl implements StorageService {
|
||||
}
|
||||
}
|
||||
|
||||
if(!sameMd5){
|
||||
if (!sameMd5) {
|
||||
// 新文件时,执行写入逻辑
|
||||
storageEngineService.createFile(saveDTO, inputStream);
|
||||
}
|
||||
@ -230,7 +233,7 @@ public class StorageServiceImpl implements StorageService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<FileMetadataInfoVo,byte[]> read(String fileKey) {
|
||||
public Pair<FileMetadataInfoVo, byte[]> read(String fileKey) {
|
||||
return storageEngineService.read(fileKey);
|
||||
}
|
||||
|
||||
@ -239,18 +242,18 @@ public class StorageServiceImpl implements StorageService {
|
||||
return storageEngineService.remove(fileKey);
|
||||
}
|
||||
|
||||
FileMetadataInfoSaveDTO buildSaveDto(String fullFileName, Boolean isPrivate, String userId, byte[] fileBytes){
|
||||
FileMetadataInfoSaveDTO buildSaveDto(String fullFileName, Boolean isPrivate, String userId, byte[] fileBytes) {
|
||||
|
||||
if(null==fileBytes){
|
||||
if (null == fileBytes) {
|
||||
throw new MinioPlusException(MinioPlusErrorCode.FILE_BYTES_FAILED);
|
||||
}
|
||||
// 计算文件MD5值
|
||||
String md5 = SecureUtil.md5().digestHex(fileBytes);
|
||||
|
||||
return buildSaveDto(fullFileName,md5,fileBytes.length,isPrivate,userId);
|
||||
return buildSaveDto(fullFileName, md5, fileBytes.length, isPrivate, userId);
|
||||
}
|
||||
|
||||
FileMetadataInfoSaveDTO buildSaveDto(String fullFileName, String md5,long fileSize,Boolean isPrivate, String userId){
|
||||
FileMetadataInfoSaveDTO buildSaveDto(String fullFileName, String md5, long fileSize, Boolean isPrivate, String userId) {
|
||||
|
||||
// 生成UUID作为文件KEY
|
||||
String key = IdUtil.fastSimpleUUID();
|
||||
@ -290,12 +293,13 @@ public class StorageServiceImpl implements StorageService {
|
||||
|
||||
/**
|
||||
* 重写文件地址
|
||||
*
|
||||
* @param url 文件地址
|
||||
* @return 重写后的文件地址
|
||||
*/
|
||||
private String remakeUrl(String url){
|
||||
private String remakeUrl(String url) {
|
||||
|
||||
if(CharSequenceUtil.isNotBlank(properties.getBrowserUrl())){
|
||||
if (CharSequenceUtil.isNotBlank(properties.getBrowserUrl())) {
|
||||
return url.replace(properties.getBackend(), properties.getBrowserUrl());
|
||||
}
|
||||
return url;
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.liuxp.minioplus.core.service.impl.StorageServiceImpl,\
|
||||
org.liuxp.minioplus.core.engine.impl.StorageEngineServiceImpl
|
||||
@ -0,0 +1,2 @@
|
||||
#org.liuxp.minioplus.core.service.impl.StorageServiceImpl
|
||||
#org.liuxp.minioplus.core.engine.impl.StorageEngineServiceImpl
|
||||
@ -20,7 +20,15 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!--<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>-->
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,111 @@
|
||||
package org.liuxp.minioplus.extension.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.liuxp.minioplus.api.model.vo.FileCheckResultVo;
|
||||
import org.liuxp.minioplus.api.model.vo.FilePreShardingVo;
|
||||
import org.liuxp.minioplus.extension.context.Response;
|
||||
import org.liuxp.minioplus.extension.dto.FileCheckDTO;
|
||||
import org.liuxp.minioplus.extension.dto.FileCompleteDTO;
|
||||
import org.liuxp.minioplus.extension.dto.PreShardingDTO;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 对象存储标准接口定义
|
||||
* 本类的方法是给前端使用的方法
|
||||
*
|
||||
* @author contact@liuxp.me
|
||||
* @since 2024/6/18
|
||||
*/
|
||||
@Tag(name = "MinIO Plus 接口")
|
||||
public interface StorageWebAPI {
|
||||
|
||||
/**
|
||||
* 请求地址前缀
|
||||
*/
|
||||
String ROOT_PATH = "/storage";
|
||||
|
||||
/**
|
||||
* 图标请求地址
|
||||
*/
|
||||
String ICON_PATH = "/storage/icon/";
|
||||
|
||||
/**
|
||||
* 文件预分片方法
|
||||
* 在大文件上传时,为了防止前端重复计算文件MD5值,提供该方法
|
||||
*
|
||||
* @param preShardingDTO 文件预分片入参DTO
|
||||
* @return 预分片结果
|
||||
*/
|
||||
@Operation(summary = "文件预分片")
|
||||
@PostMapping("/upload/sharding")
|
||||
@ResponseBody
|
||||
Response<FilePreShardingVo> sharding(@RequestBody @Validated PreShardingDTO preShardingDTO);
|
||||
|
||||
/**
|
||||
* 上传任务初始化
|
||||
* 上传前的预检查:秒传、分块上传和断点续传等特性均基于该方法实现
|
||||
*
|
||||
* @param fileCheckDTO 文件预检查入参
|
||||
* @return 检查结果
|
||||
*/
|
||||
@Operation(summary = "上传任务初始化")
|
||||
@PostMapping("/upload/init")
|
||||
@ResponseBody
|
||||
Response<FileCheckResultVo> init(@RequestBody @Validated FileCheckDTO fileCheckDTO);
|
||||
|
||||
/**
|
||||
* 文件上传完成
|
||||
*
|
||||
* @param fileKey 文件KEY
|
||||
* @param fileCompleteDTO 文件完成入参DTO
|
||||
* @return 是否成功
|
||||
*/
|
||||
@Operation(summary = "上传完成")
|
||||
@PostMapping("/upload/complete/{fileKey}")
|
||||
@ResponseBody
|
||||
Response<Object> complete(@PathVariable("fileKey") String fileKey, @RequestBody FileCompleteDTO fileCompleteDTO);
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*
|
||||
* @param fileKey 文件KEY
|
||||
* @return 文件下载地址
|
||||
*/
|
||||
@Operation(summary = "文件下载")
|
||||
@GetMapping("/download/{fileKey}")
|
||||
String download(@PathVariable("fileKey") String fileKey);
|
||||
|
||||
/**
|
||||
* 获取图像
|
||||
*
|
||||
* @param fileKey 文件KEY
|
||||
* @return 原图地址
|
||||
*/
|
||||
@Operation(summary = "图片预览 - 原图")
|
||||
@GetMapping("/image/{fileKey}")
|
||||
String previewOriginal(@PathVariable("fileKey") String fileKey);
|
||||
|
||||
/**
|
||||
* 文件预览
|
||||
* 当文件为图片时,返回图片的缩略图
|
||||
* 当文件不是图片时,返回文件类型图标
|
||||
*
|
||||
* @param fileKey 文件KEY
|
||||
* @return 缩略图地址
|
||||
*/
|
||||
@Operation(summary = "图片预览 - 缩略图")
|
||||
@GetMapping("/preview/{fileKey}")
|
||||
String previewMedium(@PathVariable("fileKey") String fileKey);
|
||||
|
||||
/**
|
||||
* 根据文件类型取得图标
|
||||
*
|
||||
* @param fileType 文件扩展名
|
||||
*/
|
||||
@Operation(summary = "获取图标")
|
||||
@GetMapping("/icon/{fileType}")
|
||||
void icon(@PathVariable("fileType") String fileType);
|
||||
|
||||
}
|
||||
@ -26,6 +26,13 @@
|
||||
<groupId>me.liuxp</groupId>
|
||||
<artifactId>minio-plus-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.36</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -9,14 +9,12 @@ import io.minio.errors.XmlParserException;
|
||||
import io.minio.http.Method;
|
||||
import io.minio.messages.Part;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.liuxp.minioplus.common.config.MinioPlusProperties;
|
||||
import org.liuxp.minioplus.common.enums.MinioPlusErrorCode;
|
||||
import org.liuxp.minioplus.common.exception.MinioPlusException;
|
||||
import org.liuxp.minioplus.common.config.MinioPlusProperties;
|
||||
import org.liuxp.minioplus.s3.def.ListParts;
|
||||
import org.liuxp.minioplus.s3.def.MinioS3Client;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.InvalidKeyException;
|
||||
@ -29,7 +27,6 @@ import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
@Repository
|
||||
public class MinioS3ClientImpl implements MinioS3Client {
|
||||
|
||||
/**
|
||||
@ -41,14 +38,17 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
*/
|
||||
private static final String PART_NUMBER = "partNumber";
|
||||
|
||||
@Resource
|
||||
private MinioPlusProperties properties;
|
||||
private final MinioPlusProperties properties;
|
||||
|
||||
private CustomMinioClient minioClient = null;
|
||||
|
||||
public CustomMinioClient getClient(){
|
||||
public MinioS3ClientImpl(MinioPlusProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
if(null==this.minioClient){
|
||||
public CustomMinioClient getClient() {
|
||||
|
||||
if (null == this.minioClient) {
|
||||
MinioAsyncClient client = MinioAsyncClient.builder()
|
||||
.endpoint(properties.getBackend())
|
||||
.credentials(properties.getKey(), properties.getSecret())
|
||||
@ -63,8 +63,9 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
public Boolean bucketExists(String bucketName) {
|
||||
try {
|
||||
return this.getClient().bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()).get();
|
||||
} catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException | ExecutionException | InterruptedException e) {
|
||||
log.error(MinioPlusErrorCode.BUCKET_EXISTS_FAILED.getMessage()+":{}", e.getMessage(), e);
|
||||
} catch (InsufficientDataException | InternalException | InvalidKeyException | IOException |
|
||||
NoSuchAlgorithmException | XmlParserException | ExecutionException | InterruptedException e) {
|
||||
log.error("{}:{}", MinioPlusErrorCode.BUCKET_EXISTS_FAILED.getMessage(), e.getMessage(), e);
|
||||
throw new MinioPlusException(MinioPlusErrorCode.BUCKET_EXISTS_FAILED);
|
||||
}
|
||||
}
|
||||
@ -78,7 +79,7 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
this.getClient().makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(MinioPlusErrorCode.MAKE_BUCKET_FAILED.getMessage()+":{}", e.getMessage(), e);
|
||||
log.error("{}:{}", MinioPlusErrorCode.MAKE_BUCKET_FAILED.getMessage(), e.getMessage(), e);
|
||||
throw new MinioPlusException(MinioPlusErrorCode.MAKE_BUCKET_FAILED);
|
||||
}
|
||||
}
|
||||
@ -89,7 +90,7 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
CreateMultipartUploadResponse createMultipartUploadResponse = this.getClient().createMultipartUpload(bucketName, null, objectName, null, null);
|
||||
return createMultipartUploadResponse.result().uploadId();
|
||||
} catch (Exception e) {
|
||||
log.error(MinioPlusErrorCode.CREATE_MULTIPART_UPLOAD_FAILED.getMessage()+":{}", e.getMessage(), e);
|
||||
log.error("{}:{}", MinioPlusErrorCode.CREATE_MULTIPART_UPLOAD_FAILED.getMessage(), e.getMessage(), e);
|
||||
throw new MinioPlusException(MinioPlusErrorCode.CREATE_MULTIPART_UPLOAD_FAILED);
|
||||
}
|
||||
}
|
||||
@ -100,7 +101,7 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
Part[] partArray = new Part[parts.size()];
|
||||
|
||||
for (int i = 0; i < parts.size(); i++) {
|
||||
partArray[i] = new Part(parts.get(i).getPartNumber(),parts.get(i).getEtag());
|
||||
partArray[i] = new Part(parts.get(i).getPartNumber(), parts.get(i).getEtag());
|
||||
}
|
||||
|
||||
try {
|
||||
@ -108,7 +109,7 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
, objectName, uploadId, partArray, null, null);
|
||||
return objectWriteResponse != null;
|
||||
} catch (Exception e) {
|
||||
log.error(MinioPlusErrorCode.COMPLETE_MULTIPART_FAILED.getMessage()+",uploadId:{},ObjectName:{},失败原因:{},", uploadId, objectName, e.getMessage(), e);
|
||||
log.error("{},uploadId:{},ObjectName:{},失败原因:{},", MinioPlusErrorCode.COMPLETE_MULTIPART_FAILED.getMessage(), uploadId, objectName, e.getMessage(), e);
|
||||
throw new MinioPlusException(MinioPlusErrorCode.COMPLETE_MULTIPART_FAILED);
|
||||
}
|
||||
}
|
||||
@ -134,14 +135,14 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
|
||||
} catch (Exception e) {
|
||||
// 查询分片失败,打印日志,返回空的分片信息
|
||||
log.error(MinioPlusErrorCode.LIST_PARTS_FAILED.getMessage()+":{}", e.getMessage());
|
||||
log.error("{}:{}", MinioPlusErrorCode.LIST_PARTS_FAILED.getMessage(), e.getMessage());
|
||||
}
|
||||
|
||||
return listParts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploadObjectUrl(String bucketName, String objectName, String uploadId,String partNumber) {
|
||||
public String getUploadObjectUrl(String bucketName, String objectName, String uploadId, String partNumber) {
|
||||
|
||||
Map<String, String> queryParams = Maps.newHashMapWithExpectedSize(2);
|
||||
queryParams.put(UPLOAD_ID, uploadId);
|
||||
@ -157,7 +158,7 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
.extraQueryParams(queryParams)
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
log.error(MinioPlusErrorCode.CREATE_UPLOAD_URL_FAILED.getMessage()+":{}", e.getMessage(), e);
|
||||
log.error("{}:{}", MinioPlusErrorCode.CREATE_UPLOAD_URL_FAILED.getMessage(), e.getMessage(), e);
|
||||
throw new MinioPlusException(MinioPlusErrorCode.CREATE_UPLOAD_URL_FAILED);
|
||||
}
|
||||
}
|
||||
@ -165,7 +166,7 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
@Override
|
||||
public String getDownloadUrl(String fileName, String contentType, String bucketName, String objectName) {
|
||||
Map<String, String> reqParams = new HashMap<>();
|
||||
reqParams.put("response-content-disposition", "attachment;filename=\""+fileName+"\"");
|
||||
reqParams.put("response-content-disposition", "attachment;filename=\"" + fileName + "\"");
|
||||
reqParams.put("response-content-type", contentType);
|
||||
|
||||
try {
|
||||
@ -178,7 +179,7 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
.extraQueryParams(reqParams)
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
log.error(MinioPlusErrorCode.CREATE_DOWNLOAD_URL_FAILED.getMessage()+":{}", e.getMessage(), e);
|
||||
log.error("{}:{}", MinioPlusErrorCode.CREATE_DOWNLOAD_URL_FAILED.getMessage(), e.getMessage(), e);
|
||||
throw new MinioPlusException(MinioPlusErrorCode.CREATE_DOWNLOAD_URL_FAILED);
|
||||
}
|
||||
}
|
||||
@ -199,14 +200,14 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
.extraQueryParams(reqParams)
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
log.error(MinioPlusErrorCode.CREATE_PREVIEW_URL_FAILED.getMessage()+":{}", e.getMessage(), e);
|
||||
log.error("{}:{}", MinioPlusErrorCode.CREATE_PREVIEW_URL_FAILED.getMessage(), e.getMessage(), e);
|
||||
throw new MinioPlusException(MinioPlusErrorCode.CREATE_PREVIEW_URL_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean putObject(String bucketName, String objectName, InputStream stream, long size, String contentType) {
|
||||
try{
|
||||
try {
|
||||
|
||||
// 检查存储桶是否已经存在
|
||||
boolean isExist = this.getClient().bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()).get();
|
||||
@ -220,12 +221,13 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
this.getClient().putObject(PutObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.stream(stream,size,0L)
|
||||
.stream(stream, size, 0L)
|
||||
.contentType(contentType)
|
||||
.build());
|
||||
|
||||
} catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException | ExecutionException | InterruptedException e) {
|
||||
log.error(MinioPlusErrorCode.WRITE_FAILED.getMessage(),e);
|
||||
} catch (InsufficientDataException | InternalException | InvalidKeyException | IOException |
|
||||
NoSuchAlgorithmException | XmlParserException | ExecutionException | InterruptedException e) {
|
||||
log.error(MinioPlusErrorCode.WRITE_FAILED.getMessage(), e);
|
||||
throw new MinioPlusException(MinioPlusErrorCode.WRITE_FAILED);
|
||||
}
|
||||
|
||||
@ -239,7 +241,7 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
// 文件流转换为字节码
|
||||
return IoUtil.readBytes(inputStream);
|
||||
} catch (Exception e) {
|
||||
log.error(MinioPlusErrorCode.READ_FAILED.getMessage(),e);
|
||||
log.error(MinioPlusErrorCode.READ_FAILED.getMessage(), e);
|
||||
throw new MinioPlusException(MinioPlusErrorCode.READ_FAILED);
|
||||
}
|
||||
}
|
||||
@ -249,7 +251,7 @@ public class MinioS3ClientImpl implements MinioS3Client {
|
||||
try {
|
||||
this.getClient().removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
|
||||
} catch (Exception e) {
|
||||
log.error(MinioPlusErrorCode.DELETE_FAILED.getMessage(),e);
|
||||
log.error(MinioPlusErrorCode.DELETE_FAILED.getMessage(), e);
|
||||
throw new MinioPlusException(MinioPlusErrorCode.DELETE_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.liuxp.minioplus.s3.official.MinioS3ClientImpl
|
||||
@ -0,0 +1 @@
|
||||
org.liuxp.minioplus.s3.official.MinioS3ClientImpl
|
||||
@ -21,7 +21,8 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${spring-boot2.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.liuxp</groupId>
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
package org.liuxp.minioplus.extension.controller;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.liuxp.minioplus.api.StorageService;
|
||||
import org.liuxp.minioplus.api.model.vo.CompleteResultVo;
|
||||
@ -20,50 +18,48 @@ import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
/**
|
||||
* 对象存储标准接口定义
|
||||
* 本类的方法是给前端使用的方法
|
||||
*
|
||||
* @author contact@liuxp.me
|
||||
* @since 2024/6/18
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/storage")
|
||||
@Tag(name = "MinIO Plus 接口")
|
||||
@Slf4j
|
||||
public class StorageController {
|
||||
@Controller
|
||||
@RequestMapping(StorageWebAPI.ROOT_PATH)
|
||||
public class StorageController implements StorageWebAPI {
|
||||
|
||||
/**
|
||||
* 重定向
|
||||
*/
|
||||
private static final String REDIRECT_PREFIX = "redirect:";
|
||||
|
||||
/**
|
||||
* 图标请求地址
|
||||
*/
|
||||
private static final String ICON_PATH = "/storage/icon/";
|
||||
|
||||
/**
|
||||
* 存储引擎Service接口定义
|
||||
*/
|
||||
@Resource
|
||||
private StorageService storageService;
|
||||
private final StorageService storageService;
|
||||
|
||||
public StorageController(StorageService storageService) {
|
||||
this.storageService = storageService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件预分片方法
|
||||
* 在大文件上传时,为了防止前端重复计算文件MD5值,提供该方法
|
||||
*
|
||||
* @param preShardingDTO 文件预分片入参DTO
|
||||
* @return 预分片结果
|
||||
*/
|
||||
@Operation(summary = "文件预分片")
|
||||
@PostMapping("/upload/sharding")
|
||||
@ResponseBody
|
||||
public Response<FilePreShardingVo> sharding(@RequestBody @Validated PreShardingDTO preShardingDTO){
|
||||
@Override
|
||||
public Response<FilePreShardingVo> sharding(@RequestBody @Validated PreShardingDTO preShardingDTO) {
|
||||
|
||||
FilePreShardingVo resultVo = storageService.sharding(preShardingDTO.getFileSize());
|
||||
|
||||
@ -73,51 +69,49 @@ public class StorageController {
|
||||
/**
|
||||
* 上传任务初始化
|
||||
* 上传前的预检查:秒传、分块上传和断点续传等特性均基于该方法实现
|
||||
*
|
||||
* @param fileCheckDTO 文件预检查入参
|
||||
* @return 检查结果
|
||||
*/
|
||||
@Operation(summary = "上传任务初始化")
|
||||
@PostMapping("/upload/init")
|
||||
@ResponseBody
|
||||
public Response<FileCheckResultVo> init(@RequestBody @Validated FileCheckDTO fileCheckDTO) {
|
||||
@Override
|
||||
public Response<FileCheckResultVo> init(FileCheckDTO fileCheckDTO) {
|
||||
|
||||
// 取得当前登录用户信息
|
||||
String userId = UserHolder.get();
|
||||
|
||||
FileCheckResultVo resultVo = storageService.init(fileCheckDTO.getFileMd5(),fileCheckDTO.getFullFileName(),fileCheckDTO.getFileSize(),fileCheckDTO.getIsPrivate(),userId);
|
||||
FileCheckResultVo resultVo = storageService.init(fileCheckDTO.getFileMd5(), fileCheckDTO.getFullFileName(), fileCheckDTO.getFileSize(), fileCheckDTO.getIsPrivate(), userId);
|
||||
|
||||
return Response.success(resultVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传完成
|
||||
*
|
||||
* @param fileKey 文件KEY
|
||||
* @param fileCompleteDTO 文件完成入参DTO
|
||||
* @return 是否成功
|
||||
*/
|
||||
@Operation(summary = "上传完成")
|
||||
@PostMapping("/upload/complete/{fileKey}")
|
||||
@ResponseBody
|
||||
public Response<Object> complete(@PathVariable("fileKey") String fileKey, @RequestBody FileCompleteDTO fileCompleteDTO) {
|
||||
@Override
|
||||
public Response<Object> complete(String fileKey, FileCompleteDTO fileCompleteDTO) {
|
||||
|
||||
// 取得当前登录用户信息
|
||||
String userId = UserHolder.get();
|
||||
|
||||
// 打印调试日志
|
||||
log.debug("合并文件开始fileKey="+fileKey+",partMd5List="+fileCompleteDTO.getPartMd5List());
|
||||
CompleteResultVo completeResultVo = storageService.complete(fileKey,fileCompleteDTO.getPartMd5List(),userId);
|
||||
log.debug("合并文件开始fileKey=" + fileKey + ",partMd5List=" + fileCompleteDTO.getPartMd5List());
|
||||
CompleteResultVo completeResultVo = storageService.complete(fileKey, fileCompleteDTO.getPartMd5List(), userId);
|
||||
|
||||
return Response.success(completeResultVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*
|
||||
* @param fileKey 文件KEY
|
||||
* @return 文件下载地址
|
||||
*/
|
||||
@Operation(summary = "文件下载")
|
||||
@GetMapping("/download/{fileKey}")
|
||||
public String download(@PathVariable String fileKey) {
|
||||
@Override
|
||||
public String download(String fileKey) {
|
||||
|
||||
// 取得当前登录用户信息
|
||||
String userId = UserHolder.get();
|
||||
@ -128,12 +122,12 @@ public class StorageController {
|
||||
|
||||
/**
|
||||
* 获取图像
|
||||
*
|
||||
* @param fileKey 文件KEY
|
||||
* @return 原图地址
|
||||
*/
|
||||
@Operation(summary = "图片预览 - 原图")
|
||||
@GetMapping("/image/{fileKey}")
|
||||
public String previewOriginal(@PathVariable String fileKey) {
|
||||
@Override
|
||||
public String previewOriginal(String fileKey) {
|
||||
|
||||
// 取得当前登录用户信息
|
||||
String userId = UserHolder.get();
|
||||
@ -146,18 +140,18 @@ public class StorageController {
|
||||
* 文件预览
|
||||
* 当文件为图片时,返回图片的缩略图
|
||||
* 当文件不是图片时,返回文件类型图标
|
||||
*
|
||||
* @param fileKey 文件KEY
|
||||
* @return 缩略图地址
|
||||
*/
|
||||
@Operation(summary = "图片预览 - 缩略图")
|
||||
@GetMapping("/preview/{fileKey}")
|
||||
public String previewMedium(@PathVariable String fileKey) {
|
||||
@Override
|
||||
public String previewMedium(String fileKey) {
|
||||
|
||||
// 取得当前登录用户信息
|
||||
String userId = UserHolder.get();
|
||||
|
||||
String url = storageService.preview(fileKey, userId);
|
||||
if(url.length()<10){
|
||||
if (url.length() < 10) {
|
||||
// 当返回值为文件类型时,取得图标
|
||||
url = ICON_PATH + url;
|
||||
}
|
||||
@ -168,30 +162,27 @@ public class StorageController {
|
||||
|
||||
/**
|
||||
* 根据文件类型取得图标
|
||||
* @param response HttpServletResponse
|
||||
*
|
||||
* @param fileType 文件扩展名
|
||||
*/
|
||||
@Operation(summary = "获取图标")
|
||||
@GetMapping("/icon/{fileType}")
|
||||
public void icon(HttpServletResponse response,@PathVariable String fileType) {
|
||||
@Override
|
||||
public void icon(String fileType) {
|
||||
try {
|
||||
|
||||
// 根据文件后缀取得桶
|
||||
String storageBucket = StorageBucketEnums.getBucketByFileSuffix(fileType);
|
||||
|
||||
ClassPathResource cpr = new ClassPathResource(storageBucket+".png");
|
||||
ClassPathResource cpr = new ClassPathResource(storageBucket + ".png");
|
||||
|
||||
byte[] bytes = FileCopyUtils.copyToByteArray(cpr.getInputStream());
|
||||
|
||||
response.setHeader("content-disposition", "inline");
|
||||
response.setHeader("Content-Length", String.valueOf(bytes.length));
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
|
||||
|
||||
IoUtil.copy(inputStream, response.getOutputStream());
|
||||
inputStream.close();
|
||||
|
||||
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
attr.getResponse().setHeader("content-disposition", "inline");
|
||||
attr.getResponse().setHeader("Content-Length", String.valueOf(bytes.length));
|
||||
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
|
||||
IoUtil.copy(inputStream, attr.getResponse().getOutputStream());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(MinioPlusErrorCode.FILE_ICON_FAILED.getMessage(),e);
|
||||
log.error(MinioPlusErrorCode.FILE_ICON_FAILED.getMessage(), e);
|
||||
// 图标获取失败
|
||||
throw new MinioPlusException(MinioPlusErrorCode.FILE_ICON_FAILED);
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.liuxp.minioplus.common.config.MinioPlusAutoConfiguration
|
||||
@ -0,0 +1 @@
|
||||
org.liuxp.minioplus.common.config.MinioPlusAutoConfiguration
|
||||
@ -15,11 +15,21 @@
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<!--<maven.compiler.compilerVersion>17</maven.compiler.compilerVersion>-->
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${spring-boot3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>me.liuxp</groupId>
|
||||
<artifactId>minio-plus-extension</artifactId>
|
||||
|
||||
@ -0,0 +1,191 @@
|
||||
package org.liuxp.minioplus.extension.controller;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.liuxp.minioplus.api.StorageService;
|
||||
import org.liuxp.minioplus.api.model.vo.CompleteResultVo;
|
||||
import org.liuxp.minioplus.api.model.vo.FileCheckResultVo;
|
||||
import org.liuxp.minioplus.api.model.vo.FilePreShardingVo;
|
||||
import org.liuxp.minioplus.common.enums.MinioPlusErrorCode;
|
||||
import org.liuxp.minioplus.common.enums.StorageBucketEnums;
|
||||
import org.liuxp.minioplus.common.exception.MinioPlusException;
|
||||
import org.liuxp.minioplus.extension.context.Response;
|
||||
import org.liuxp.minioplus.extension.context.UserHolder;
|
||||
import org.liuxp.minioplus.extension.dto.FileCheckDTO;
|
||||
import org.liuxp.minioplus.extension.dto.FileCompleteDTO;
|
||||
import org.liuxp.minioplus.extension.dto.PreShardingDTO;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
/**
|
||||
* 对象存储标准接口定义
|
||||
* 本类的方法是给前端使用的方法
|
||||
*
|
||||
* @author contact@liuxp.me
|
||||
* @since 2024/6/18
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequestMapping(StorageWebAPI.ROOT_PATH)
|
||||
public class StorageController implements StorageWebAPI {
|
||||
|
||||
/**
|
||||
* 重定向
|
||||
*/
|
||||
private static final String REDIRECT_PREFIX = "redirect:";
|
||||
|
||||
/**
|
||||
* 存储引擎Service接口定义
|
||||
*/
|
||||
private final StorageService storageService;
|
||||
|
||||
public StorageController(StorageService storageService) {
|
||||
this.storageService = storageService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件预分片方法
|
||||
* 在大文件上传时,为了防止前端重复计算文件MD5值,提供该方法
|
||||
*
|
||||
* @param preShardingDTO 文件预分片入参DTO
|
||||
* @return 预分片结果
|
||||
*/
|
||||
@Override
|
||||
public Response<FilePreShardingVo> sharding(@RequestBody @Validated PreShardingDTO preShardingDTO) {
|
||||
|
||||
FilePreShardingVo resultVo = storageService.sharding(preShardingDTO.getFileSize());
|
||||
|
||||
return Response.success(resultVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传任务初始化
|
||||
* 上传前的预检查:秒传、分块上传和断点续传等特性均基于该方法实现
|
||||
*
|
||||
* @param fileCheckDTO 文件预检查入参
|
||||
* @return 检查结果
|
||||
*/
|
||||
@Override
|
||||
public Response<FileCheckResultVo> init(FileCheckDTO fileCheckDTO) {
|
||||
|
||||
// 取得当前登录用户信息
|
||||
String userId = UserHolder.get();
|
||||
|
||||
FileCheckResultVo resultVo = storageService.init(fileCheckDTO.getFileMd5(), fileCheckDTO.getFullFileName(), fileCheckDTO.getFileSize(), fileCheckDTO.getIsPrivate(), userId);
|
||||
|
||||
return Response.success(resultVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传完成
|
||||
*
|
||||
* @param fileKey 文件KEY
|
||||
* @param fileCompleteDTO 文件完成入参DTO
|
||||
* @return 是否成功
|
||||
*/
|
||||
@Override
|
||||
public Response<Object> complete(String fileKey, FileCompleteDTO fileCompleteDTO) {
|
||||
|
||||
// 取得当前登录用户信息
|
||||
String userId = UserHolder.get();
|
||||
|
||||
// 打印调试日志
|
||||
log.debug("合并文件开始fileKey=" + fileKey + ",partMd5List=" + fileCompleteDTO.getPartMd5List());
|
||||
CompleteResultVo completeResultVo = storageService.complete(fileKey, fileCompleteDTO.getPartMd5List(), userId);
|
||||
|
||||
return Response.success(completeResultVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*
|
||||
* @param fileKey 文件KEY
|
||||
* @return 文件下载地址
|
||||
*/
|
||||
@Override
|
||||
public String download(String fileKey) {
|
||||
|
||||
// 取得当前登录用户信息
|
||||
String userId = UserHolder.get();
|
||||
|
||||
// 取得文件读取路径
|
||||
return REDIRECT_PREFIX + storageService.download(fileKey, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图像
|
||||
*
|
||||
* @param fileKey 文件KEY
|
||||
* @return 原图地址
|
||||
*/
|
||||
@Override
|
||||
public String previewOriginal(String fileKey) {
|
||||
|
||||
// 取得当前登录用户信息
|
||||
String userId = UserHolder.get();
|
||||
|
||||
// 取得文件读取路径
|
||||
return REDIRECT_PREFIX + storageService.image(fileKey, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件预览
|
||||
* 当文件为图片时,返回图片的缩略图
|
||||
* 当文件不是图片时,返回文件类型图标
|
||||
*
|
||||
* @param fileKey 文件KEY
|
||||
* @return 缩略图地址
|
||||
*/
|
||||
@Override
|
||||
public String previewMedium(String fileKey) {
|
||||
|
||||
// 取得当前登录用户信息
|
||||
String userId = UserHolder.get();
|
||||
|
||||
String url = storageService.preview(fileKey, userId);
|
||||
if (url.length() < 10) {
|
||||
// 当返回值为文件类型时,取得图标
|
||||
url = ICON_PATH + url;
|
||||
}
|
||||
|
||||
// 取得文件读取路径
|
||||
return REDIRECT_PREFIX + url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件类型取得图标
|
||||
*
|
||||
* @param fileType 文件扩展名
|
||||
*/
|
||||
@Override
|
||||
public void icon(String fileType) {
|
||||
try {
|
||||
// 根据文件后缀取得桶
|
||||
String storageBucket = StorageBucketEnums.getBucketByFileSuffix(fileType);
|
||||
|
||||
ClassPathResource cpr = new ClassPathResource(storageBucket + ".png");
|
||||
|
||||
byte[] bytes = FileCopyUtils.copyToByteArray(cpr.getInputStream());
|
||||
|
||||
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
attr.getResponse().setHeader("content-disposition", "inline");
|
||||
attr.getResponse().setHeader("Content-Length", String.valueOf(bytes.length));
|
||||
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
|
||||
IoUtil.copy(inputStream, attr.getResponse().getOutputStream());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(MinioPlusErrorCode.FILE_ICON_FAILED.getMessage(), e);
|
||||
// 图标获取失败
|
||||
throw new MinioPlusException(MinioPlusErrorCode.FILE_ICON_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -22,6 +22,8 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.liuxp</groupId>
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.liuxp.minioplus.common.config.MinioPlusAutoConfiguration
|
||||
@ -0,0 +1 @@
|
||||
org.liuxp.minioplus.common.config.MinioPlusAutoConfiguration
|
||||
49
pom.xml
49
pom.xml
@ -50,9 +50,13 @@
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven-source-plugin.version>3.0.1</maven-source-plugin.version>
|
||||
<revision>0.1.5</revision>
|
||||
<spring-boot.version>2.7.18</spring-boot.version>
|
||||
<spring-boot2.version>2.7.18</spring-boot2.version>
|
||||
<spring-boot3.version>3.3.2</spring-boot3.version>
|
||||
<spring-boot.version>${spring-boot2.version}</spring-boot.version>
|
||||
<mybatisplus.version>3.5.7</mybatisplus.version>
|
||||
<mysql-connector-j.version>8.0.33</mysql-connector-j.version>
|
||||
<lombok.version>1.18.32</lombok.version>
|
||||
<jakarta-servlet-api.version>6.0.0</jakarta-servlet-api.version>
|
||||
<hutool.version>5.8.28</hutool.version>
|
||||
<knife4j.version>4.4.0</knife4j.version>
|
||||
<swagger.version>2.2.8</swagger.version>
|
||||
@ -63,24 +67,44 @@
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- spring-boot-dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!-- mybatis-plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatisplus.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<version>${mybatisplus.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>${mysql-connector-j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<version>${jakarta-servlet-api.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</dependency>
|
||||
<!--<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.36</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.36</version>
|
||||
</dependency>-->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
@ -103,13 +127,6 @@
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
</dependency>
|
||||
<!-- mybatis -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus</artifactId>
|
||||
<version>${mybatisplus.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- minio -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user