feat: 修改文件重做

This commit is contained in:
bunny 2024-10-14 14:13:50 +08:00
parent e3a1f75da2
commit 4b37f707a3
14 changed files with 273 additions and 61 deletions

View File

@ -4,10 +4,9 @@ import cn.bunny.common.service.exception.BunnyException;
import cn.bunny.dao.pojo.common.MinioFilePath;
import cn.bunny.dao.pojo.constant.MinioConstant;
import cn.bunny.dao.pojo.result.ResultCodeEnum;
import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -18,6 +17,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.UUID;
/**
@ -29,13 +29,14 @@ import java.util.UUID;
public class MinioUtil {
@Autowired
private MinioProperties properties;
@Autowired
private MinioClient minioClient;
/**
* 获取Minio文件路径
*/
public static MinioFilePath getMinioFilePath(String buckName, String minioPreType, MultipartFile file) {
public static MinioFilePath initUploadFile4MinioFilePath(String buckName, String minioPreType, MultipartFile file) {
String uuid = UUID.randomUUID().toString();
// 定义日期时间格式
LocalDateTime currentDateTime = LocalDateTime.now();
@ -74,11 +75,10 @@ public class MinioUtil {
/**
* * 上传文件并返回处理信息
*/
public MinioFilePath getUploadMinioObjectFilePath(MultipartFile file, String minioPreType) throws IOException {
// 如果buckName为空设置为默认的桶
public MinioFilePath uploadObject4FilePath(MultipartFile file, String minioPreType) throws IOException {
String bucketName = properties.getBucketName();
if (file != null) {
MinioFilePath minioFile = getMinioFilePath(bucketName, minioPreType, file);
MinioFilePath minioFile = initUploadFile4MinioFilePath(bucketName, minioPreType, file);
String filepath = minioFile.getFilepath();
// 上传对象
@ -137,4 +137,39 @@ public class MinioUtil {
throw new BunnyException(ResultCodeEnum.UPDATE_ERROR);
}
}
}
/**
* * 删除目标文件
*
* @param list 对象文件列表
*/
public void removeObjects(List<String> list) {
try {
String bucketName = properties.getBucketName();
List<DeleteObject> objectList = list.stream().map(DeleteObject::new).toList();
Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objectList).build());
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
throw new BunnyException("Error in deleting object " + error.objectName() + "; " + error.message());
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
/**
* * 更新文件
*
* @param bucketName 桶名称
* @param filepath 文件路径
* @param file 文件
*/
public void updateFile(String bucketName, String filepath, MultipartFile file) {
try {
putObject(bucketName, filepath, file.getInputStream(), file.getSize());
} catch (IOException e) {
throw new BunnyException(e.getMessage());
}
}
}

View File

@ -1,12 +1,17 @@
package cn.bunny.dao.dto.system.files;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@Data
@AllArgsConstructor
@ -15,22 +20,18 @@ import lombok.NoArgsConstructor;
@Schema(name = "FilesAddDto对象", title = "文件", description = "文件管理")
public class FilesAddDto {
@Schema(name = "filename", title = "文件的名称")
@NotBlank(message = "文件的名称不能为空")
@NotNull(message = "文件的名称不能为空")
private String filename;
@Schema(name = "filepath", title = "文件在服务器上的存储路径")
@NotBlank(message = "存储路径不能为空")
@NotNull(message = "存储路径不能为空")
private String filepath;
@Schema(name = "fileType", title = "文件的MIME类型")
@NotBlank(message = "文件类型不能为空")
@NotNull(message = "文件类型不能为空")
private String fileType;
@Schema(name = "downloadCount", title = "下载数量")
@Min(value = 0L, message = "最小值为0")
private Integer downloadCount = 0;
@Schema(name = "files", title = "文件")
@NotEmpty(message = "文件不能为空")
@NotNull(message = "文件不能为空")
private List<MultipartFile> files;
}

View File

@ -1,12 +1,14 @@
package cn.bunny.dao.dto.system.files;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;
@Data
@AllArgsConstructor
@ -24,17 +26,16 @@ public class FilesUpdateDto {
@NotNull(message = "文件的名称不能为空")
private String filename;
@Schema(name = "filepath", title = "文件在服务器上的存储路径")
@NotBlank(message = "存储路径不能为空")
@NotNull(message = "存储路径不能为空")
private String filepath;
@Schema(name = "fileType", title = "文件的MIME类型")
@NotBlank(message = "文件类型不能为空")
@NotNull(message = "文件类型不能为空")
private String fileType;
@Schema(name = "downloadCount", title = "下载数量")
@Min(value = 0L, message = "最小值为0")
private Integer downloadCount;
@Schema(name = "file", title = "文件")
private MultipartFile files;
}

View File

@ -14,7 +14,7 @@ public class MinioConstant {
public static final String feedback = "feedback";
public static final String articleCovers = "articleCovers";
public static final String articleAttachment = "articleAttachment";
private static final Map<String, String> typeMap = new HashMap<>();
public static final Map<String, String> typeMap = new HashMap<>();
static {
typeMap.put(favicon, "/favicon/");

10
pom.xml
View File

@ -41,6 +41,8 @@
<velocity-tools.version>3.1</velocity-tools.version>
<HikariCP.version>5.1.0</HikariCP.version>
<dynamic.datasource.version>4.3.1</dynamic.datasource.version>
<jackson-dataType.version>2.12.3</jackson-dataType.version>
<quartz-scheduler.version>2.3.2</quartz-scheduler.version>
</properties>
<dependencyManagement>
<dependencies>
@ -148,7 +150,13 @@
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.12.3</version>
<version>${jackson-dataType.version}</version>
</dependency>
<!-- quartz定时任务 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz-scheduler.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -76,6 +76,22 @@
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
</dependency>
<!-- quartz定时任务 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!-- 直接找不到上下文 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>6.1.6</version>
</dependency>
</dependencies>
<build>

View File

@ -5,6 +5,7 @@ import cn.bunny.dao.dto.system.files.FilesAddDto;
import cn.bunny.dao.dto.system.files.FilesDto;
import cn.bunny.dao.dto.system.files.FilesUpdateDto;
import cn.bunny.dao.entity.system.Files;
import cn.bunny.dao.pojo.constant.MinioConstant;
import cn.bunny.dao.pojo.result.PageResult;
import cn.bunny.dao.pojo.result.Result;
import cn.bunny.dao.pojo.result.ResultCodeEnum;
@ -22,6 +23,8 @@ import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* <p>
@ -58,6 +61,22 @@ public class FilesController {
return filesService.downloadFilesByFileId(fileId);
}
@Operation(summary = "获取所有文件类型", description = "获取所有文件类型")
@GetMapping("getAllMediaTypes")
public Mono<Result<Set<String>>> getAllMediaTypes() {
Set<String> list = filesService.getAllMediaTypes();
return Mono.just(Result.success(list));
}
@Operation(summary = "获取所有文件存储基础路径", description = "获取所有文件存储基础路径")
@GetMapping("getAllFilesStoragePath")
public Mono<Result<List<String>>> getAllFilesStoragePath() {
Map<String, String> typeMap = MinioConstant.typeMap;
List<String> list = typeMap.keySet().stream().toList();
return Mono.just(Result.success(list));
}
@Operation(summary = "根据文件名下载文件", description = "根据文件名下载文件")
@GetMapping("downloadFilesByFilepath")
public ResponseEntity<byte[]> downloadFilesByFilepath(String filepath) {
@ -66,14 +85,14 @@ public class FilesController {
@Operation(summary = "更新系统文件表", description = "更新系统文件表")
@PutMapping("updateFiles")
public Mono<Result<String>> updateFiles(@Valid @RequestBody FilesUpdateDto dto) {
public Result<String> updateFiles(@Valid FilesUpdateDto dto) {
filesService.updateFiles(dto);
return Mono.just(Result.success(ResultCodeEnum.UPDATE_SUCCESS));
return Result.success(ResultCodeEnum.UPDATE_SUCCESS);
}
@Operation(summary = "添加系统文件表", description = "添加系统文件表")
@PostMapping("addFiles")
public Mono<Result<String>> addFiles(@Valid @RequestBody FilesAddDto dto) {
public Mono<Result<String>> addFiles(@Valid FilesAddDto dto) {
filesService.addFiles(dto);
return Mono.just(Result.success(ResultCodeEnum.ADD_SUCCESS));
}
@ -87,8 +106,8 @@ public class FilesController {
@Operation(summary = "删除系统文件表", description = "删除系统文件表")
@DeleteMapping("deleteFiles")
public Mono<Result<String>> deleteFiles(@RequestBody List<Long> ids) {
public Result<String> deleteFiles(@RequestBody List<Long> ids) {
filesService.deleteFiles(ids);
return Mono.just(Result.success(ResultCodeEnum.DELETE_SUCCESS));
return Result.success(ResultCodeEnum.DELETE_SUCCESS);
}
}

View File

@ -43,7 +43,7 @@ public class FileFactory {
String filename = file.getOriginalFilename();
// 上传文件
MinioFilePath minioFIlePath = minioUtil.getUploadMinioObjectFilePath(file, type);
MinioFilePath minioFIlePath = minioUtil.uploadObject4FilePath(file, type);
String bucketNameFilepath = minioFIlePath.getBucketNameFilepath();
// 盘读研数据是否过大

View File

@ -14,6 +14,7 @@ import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import java.util.List;
import java.util.Set;
/**
* <p>
@ -76,4 +77,12 @@ public interface FilesService extends IService<Files> {
* @return 文件字节数组
*/
ResponseEntity<byte[]> downloadFilesByFilepath(String filepath);
/**
* * 获取所有文件类型
*
* @return 媒体文件类型列表
*/
Set<String> getAllMediaTypes();
}

View File

@ -1,12 +1,14 @@
package cn.bunny.services.service.impl;
import cn.bunny.common.service.exception.BunnyException;
import cn.bunny.common.service.utils.minio.MinioProperties;
import cn.bunny.common.service.utils.minio.MinioUtil;
import cn.bunny.dao.dto.system.files.FileUploadDto;
import cn.bunny.dao.dto.system.files.FilesAddDto;
import cn.bunny.dao.dto.system.files.FilesDto;
import cn.bunny.dao.dto.system.files.FilesUpdateDto;
import cn.bunny.dao.entity.system.Files;
import cn.bunny.dao.pojo.common.MinioFilePath;
import cn.bunny.dao.pojo.result.PageResult;
import cn.bunny.dao.pojo.result.ResultCodeEnum;
import cn.bunny.dao.vo.system.files.FileInfoVo;
@ -21,14 +23,20 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.validation.Valid;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* <p>
@ -39,15 +47,17 @@ import java.util.List;
* @since 2024-10-09 16:28:01
*/
@Service
@Transactional
public class FilesServiceImpl extends ServiceImpl<FilesMapper, Files> implements FilesService {
private final FileFactory fileFactory;
private final MinioUtil minioUtil;
@Autowired
private MinioProperties properties;
public FilesServiceImpl(FileFactory fileFactory, MinioUtil minioUtil) {
this.fileFactory = fileFactory;
this.minioUtil = minioUtil;
}
@Autowired
private FileFactory fileFactory;
@Autowired
private MinioUtil minioUtil;
/**
* * 系统文件表 服务实现类
@ -81,11 +91,25 @@ public class FilesServiceImpl extends ServiceImpl<FilesMapper, Files> implements
* @param dto 系统文件表添加
*/
@Override
public void addFiles(@Valid FilesAddDto dto) {
public void addFiles(FilesAddDto dto) {
List<Files> list = dto.getFiles().stream().map(file -> {
try {
MinioFilePath minioFilePath = minioUtil.uploadObject4FilePath(file, dto.getFilepath());
Files files = new Files();
files.setFileType(file.getContentType());
files.setFileSize(file.getSize());
files.setFilepath("/" + properties.getBucketName() + minioFilePath.getFilepath());
files.setFilename(minioFilePath.getFilename());
files.setDownloadCount(dto.getDownloadCount());
return files;
} catch (IOException e) {
throw new BunnyException(e.getMessage());
}
}).toList();
// 保存数据
Files files = new Files();
BeanUtils.copyProperties(dto, files);
save(files);
saveBatch(list);
}
/**
@ -95,8 +119,23 @@ public class FilesServiceImpl extends ServiceImpl<FilesMapper, Files> implements
*/
@Override
public void updateFiles(@Valid FilesUpdateDto dto) {
Long id = dto.getId();
MultipartFile file = dto.getFiles();
Files files = getOne(Wrappers.<Files>lambdaQuery().eq(Files::getId, id));
if (file != null) {
// 文件路径
String filePath = files.getFilepath().replace("/" + properties.getBucketName() + "/", "");
minioUtil.updateFile(properties.getBucketName(), filePath, file);
// 设置文件信息
files.setFileSize(file.getSize());
files.setFileType(file.getContentType());
}
// 更新内容
Files files = new Files();
files = new Files();
BeanUtils.copyProperties(dto, files);
updateById(files);
}
@ -124,6 +163,19 @@ public class FilesServiceImpl extends ServiceImpl<FilesMapper, Files> implements
@Override
public void deleteFiles(List<Long> ids) {
if (ids.isEmpty()) throw new BunnyException(ResultCodeEnum.REQUEST_IS_EMPTY);
// 查询文件路径
List<String> list = list(Wrappers.<Files>lambdaQuery().in(Files::getId, ids)).stream()
.map(files -> {
String filepath = files.getFilepath();
int end = filepath.indexOf("/", 1);
return filepath.substring(end + 1);
}).toList();
// 删除目标文件
minioUtil.removeObjects(list);
// 删除数据库内容
baseMapper.deleteBatchIdsWithPhysics(ids);
}
@ -179,4 +231,30 @@ public class FilesServiceImpl extends ServiceImpl<FilesMapper, Files> implements
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}
/**
* * 获取所有文件类型
*
* @return 媒体文件类型列表
*/
@Override
public Set<String> getAllMediaTypes() {
Set<String> valueList = new HashSet<>();
Class<?> mediaTypeClass = MediaType.class;
try {
for (Field declaredField : mediaTypeClass.getDeclaredFields()) {
// 获取字段属性值
declaredField.setAccessible(true);
String value = declaredField.get(null).toString();
if (value.matches("\\w+/.*")) {
valueList.add(value);
}
}
return valueList;
} catch (Exception exception) {
return Set.of();
}
}
}

View File

@ -17,3 +17,22 @@ bunny:
accessKey: bunny
secretKey: "02120212"
bucket-name: auth-admin
# rabbitmq:
# host: ${bunny.rabbitmq.host}
# port: ${bunny.rabbitmq.port}
# username: ${bunny.rabbitmq.username}
# password: ${bunny.rabbitmq.password}
# virtual-host: ${bunny.rabbitmq.virtual-host}
# publisher-confirm-type: correlated # 交换机确认
# publisher-returns: true # 队列确认
# listener:
# simple:
# acknowledge-mode: manual # 手动处理消息
# connection-timeout: 1s # 设置MQ连接超时时间
# template:
# retry:
# enabled: true # 失败重试
# initial-interval: 1000ms # 失败后初始时间
# multiplier: 1 # 失败后下次等待时长倍数 initial-interval * multiplier
# max-attempts: 3 # 最大重试次数

View File

@ -64,24 +64,13 @@ spring:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
# rabbitmq:
# host: ${bunny.rabbitmq.host}
# port: ${bunny.rabbitmq.port}
# username: ${bunny.rabbitmq.username}
# password: ${bunny.rabbitmq.password}
# virtual-host: ${bunny.rabbitmq.virtual-host}
# publisher-confirm-type: correlated # 交换机确认
# publisher-returns: true # 队列确认
# listener:
# simple:
# acknowledge-mode: manual # 手动处理消息
# connection-timeout: 1s # 设置MQ连接超时时间
# template:
# retry:
# enabled: true # 失败重试
# initial-interval: 1000ms # 失败后初始时间
# multiplier: 1 # 失败后下次等待时长倍数 initial-interval * multiplier
# max-attempts: 3 # 最大重试次数
quartz:
job-store-type: jdbc
jdbc:
initialize-schema: always
auto-startup: true
wait-for-jobs-to-complete-on-shutdown: true
overwrite-existing-jobs: false
mybatis-plus:
mapper-locations: classpath:mapper/*.xml

View File

@ -0,0 +1,11 @@
org.quartz.scheduler.instanceName=quartzScheduler
org.quartz.threadPool.threadCount=5
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.dataSource=auth_admin
org.quartz.dataSource.auth_admin.driver=com.mysql.cj.jdbc.Driver
org.quartz.dataSource.auth_admin.URL=jdbc:mysql://192.168.3.98:3304/auth_admin?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
org.quartz.dataSource.auth_admin.user=root
org.quartz.dataSource.auth_admin.password=02120212
org.quartz.dataSource.auth_admin.maxConnections=5

View File

@ -1,6 +1,13 @@
package cn.bunny.services.service.impl;
import cn.bunny.dao.pojo.constant.MinioConstant;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
class FilesServiceImplTest {
@Test
@ -15,4 +22,23 @@ class FilesServiceImplTest {
System.out.println(filename);
}
@SneakyThrows
@Test
void getAllMediaTypeTest() {
Class<?> mediaTypeClass = MediaType.class;
for (Field declaredField : mediaTypeClass.getDeclaredFields()) {
declaredField.setAccessible(true);
String value = declaredField.get(null).toString();
if (value.matches("\\w+/.*")) {
System.out.println(value);
}
}
}
@Test
void getAllPaths() {
Map<String, String> typeMap = MinioConstant.typeMap;
List<Map.Entry<String, String>> list = typeMap.entrySet().stream().toList();
System.out.println(list);
}
}