文件模块开发

This commit is contained in:
lhx
2025-12-04 18:15:21 +08:00
parent 125bde8a2d
commit d6360ec710
12 changed files with 371 additions and 5 deletions

View File

@@ -0,0 +1,34 @@
package com.dc.dc_project.config;
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class MinioConfig {
@Value("${minio.endpoint}")
public static String MINIO_ENDPOINT;
@Value("${minio.accessKey}")
public static String MINIO_ACCESS_KEY;
@Value("${minio.secretKey}")
public static String MINIO_SECRET_KEY;
@Value("${minio.bucketName}")
public static String MINIO_BUCKET_NAME;
@Bean
public MinioClient defaultClient(){
return MinioClient.builder()
.endpoint(MINIO_ENDPOINT)
.credentials(MINIO_ACCESS_KEY, MINIO_SECRET_KEY)
.build();
}
}

View File

@@ -4,10 +4,8 @@ package com.dc.dc_project.controller;
import cn.dev33.satoken.stp.StpUtil;
import com.dc.dc_project.common.ResponseResult;
import com.dc.dc_project.model.dto.*;
import com.dc.dc_project.model.pojo.PersonnelPosition;
import com.dc.dc_project.model.pojo.Position;
import com.dc.dc_project.service.PositionService;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;

View File

@@ -0,0 +1,26 @@
package com.dc.dc_project.controller.sys;
import cn.dev33.satoken.stp.StpUtil;
import com.dc.dc_project.common.ResponseResult;
import com.dc.dc_project.model.dto.FileUploadDto;
import com.dc.dc_project.service.FileService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/file")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class FileController {
private final FileService fileService;
@PostMapping("/Upload")
public ResponseResult Upload(@ModelAttribute FileUploadDto fileUploadDto) {
Long userId = StpUtil.getLoginIdAsLong();
return fileService.upload(fileUploadDto, userId);
}
}

View File

@@ -0,0 +1,61 @@
package com.dc.dc_project.enums;
import lombok.Getter;
@Getter
public enum FileType {
Unknown(0, "未知", "/unknown"),
UserAvatar(1, "用户头像", "/avatar"),
Certificate(2, "证书", "/certificate"),
Report(3, "报告", "/report"),
File(4, "文件", "/file"),
Image(5, "图片", "/image"),
UserFile(6, "用户文件", "/userFile"),
ReportFile(7, "报告文件", "/reportFile"),
Other(8, "其他", "/other");
private final Integer code;
private final String message;
private final String path;
FileType(int code, String message, String path) {
this.code = code;
this.message = message;
this.path = path;
}
public static String getMessage(int code) {
for (FileType item : FileType.values()) {
if (item.getCode() == code) {
return item.getMessage();
}
}
return null;
}
public static int getCode(String message) {
for (FileType item : FileType.values()) {
if (item.getMessage().equals(message)) {
return item.getCode();
}
}
return 0;
}
public static String getPath(int code) {
for (FileType item : FileType.values()) {
if (item.getCode() == code) {
return item.getPath();
}
}
return null;
}
public static FileType getFileType(Integer code){
for (FileType item : FileType.values()) {
if (item.getCode() == code) {
return item;
}
}
return null;
}
}

View File

@@ -0,0 +1,17 @@
package com.dc.dc_project.model.dto;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@Data
public class FileUploadDto {
private Integer type;
private List<MultipartFile> files;
private String remark;
}

View File

@@ -1,8 +1,12 @@
package com.dc.dc_project.service;
import com.dc.dc_project.common.ResponseResult;
import com.dc.dc_project.model.dto.FileUploadDto;
import com.dc.dc_project.model.pojo.File;
import com.baomidou.mybatisplus.extension.service.IService;
import java.io.IOException;
/**
* @author ADMIN
* @description 针对表【sys_file(通用文件信息表(文件存储记录))】的数据库操作Service
@@ -10,4 +14,11 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface FileService extends IService<File> {
/**
* 文件上传
* @param fileUploadDto
* @param userId
* @return
*/
ResponseResult upload(FileUploadDto fileUploadDto, Long userId);
}

View File

@@ -1,11 +1,28 @@
package com.dc.dc_project.service.impl;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dc.dc_project.common.ResponseResult;
import com.dc.dc_project.config.MinioConfig;
import com.dc.dc_project.config.exception.BusinessException;
import com.dc.dc_project.enums.FileType;
import com.dc.dc_project.model.dto.FileUploadDto;
import com.dc.dc_project.model.pojo.File;
import com.dc.dc_project.service.FileService;
import com.dc.dc_project.mapper.FileMapper;
import com.dc.dc_project.utils.FileUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author ADMIN
@@ -14,9 +31,73 @@ import lombok.extern.slf4j.Slf4j;
*/
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class FileServiceImpl extends ServiceImpl<FileMapper, File>
implements FileService{
private final MinioService minioService;
@Override
@Transactional
public ResponseResult upload(FileUploadDto fileUploadDto, Long userId) {
log.info("上传文件");
FileType fileType = FileType.getFileType(fileUploadDto.getType());
if (fileType == null) {
return ResponseResult.error("文件类型错误");
}
List<String> uploadedUrls = new ArrayList<>();
List<File> files = new ArrayList<>();
try {
// 1. 先上传文件
for (MultipartFile file : fileUploadDto.getFiles()) {
try (InputStream inputStream = file.getInputStream()) {
File sysFile = new File();
sysFile.setFileName(file.getOriginalFilename());
sysFile.setFileExt(FileUtil.getFileType(inputStream));
sysFile.setRemark(fileUploadDto.getRemark());
sysFile.setUploaderId(userId);
sysFile.setFileSize(file.getSize());
sysFile.setFileType(fileType.getCode());
// 上传到 MinIO
String url = minioService.uploadFile(
inputStream,
FileUtil.getRandomFileName(),
MinioConfig.MINIO_BUCKET_NAME,
file.getContentType(),
fileType.getPath()
);
sysFile.setFileUrl(url);
files.add(sysFile);
uploadedUrls.add(url); // 记录已上传的 URL
}
}
// 2. 保存到数据库
if (!this.saveBatch(files)) {
throw new BusinessException("数据库保存失败");
}
return ResponseResult.success(files);
} catch (Exception e) {
log.error("文件上传失败,开始清理已上传的文件", e);
// 3. 回滚:删除已上传的文件
for (String url : uploadedUrls) {
try {
minioService.deleteFile(url, MinioConfig.MINIO_BUCKET_NAME);
log.info("已删除文件: {}", url);
} catch (Exception deleteException) {
log.error("删除文件失败: {}", url, deleteException);
}
}
throw new BusinessException("文件上传失败: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,83 @@
package com.dc.dc_project.service.impl;
import com.dc.dc_project.config.MinioConfig;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.errors.*;
import io.minio.http.Method;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class MinioService {
private final MinioClient minioClient;
/**
* 上传文件
*/
public String uploadFile(InputStream inputStream, String fileName, String bucketName, String contentType, String path) {
fileName = path + "/" + fileName;
try {
minioClient.putObject(
PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(
inputStream, -1, 10485760)
.contentType(contentType)
.build());
} catch (ErrorResponseException | XmlParserException | ServerException | InvalidResponseException |
IOException | NoSuchAlgorithmException | InvalidKeyException | InternalException |
InsufficientDataException e) {
throw new RuntimeException(e);
}
return MinioConfig.MINIO_ENDPOINT + "/" + bucketName + "/" + fileName;
}
/**
* 获取临时访问连接
*/
public String getPreSignedUrl(String bucketName, String objectName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
Map<String, String> reqParams = new HashMap<String, String>();
reqParams.put("response-content-type", "application/json");
String url =
minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(objectName)
.expiry(2, TimeUnit.HOURS)
.extraQueryParams(reqParams)
.build());
return url;
}
public void deleteFile(String url, String minioBucketName) {
String objectName = url.replace(MinioConfig.MINIO_ENDPOINT + "/" + minioBucketName + "/", "");
try {
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket(minioBucketName)
.object(objectName)
.build());
} catch (ErrorResponseException | XmlParserException | ServerException | InvalidResponseException |
IOException | NoSuchAlgorithmException | InvalidKeyException | InternalException |
InsufficientDataException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -1,4 +1,4 @@
package com.dc.dc_project.service;
package com.dc.dc_project.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -13,6 +13,7 @@ import com.dc.dc_project.model.pojo.PersonnelPosition;
import com.dc.dc_project.model.pojo.Position;
import com.dc.dc_project.mapper.PositionMapper;
import com.dc.dc_project.model.vo.PositionVo;
import com.dc.dc_project.service.PositionService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -30,7 +31,7 @@ import static com.dc.dc_project.model.vo.PositionVo.potoVo;
@Service
@RequiredArgsConstructor(onConstructor_ = @__(@Autowired))
public class PositionServiceImpl extends ServiceImpl<PositionMapper, Position>
implements PositionService{
implements PositionService {
private final OrgMapper orgMapper;
private final PersonnelPositionMapper personnelPositionMapper;

View File

@@ -0,0 +1,38 @@
package com.dc.dc_project.utils;
import cn.hutool.core.io.FileTypeUtil;
import com.dc.dc_project.enums.FileType;
import com.dc.dc_project.model.pojo.File;
import java.io.InputStream;
import java.util.UUID;
public class FileUtil {
/**
* 获取随机文件名
*/
public static String getRandomFileName() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
/**
* 获取文件后缀
* @param fileName
* @date 2023/05/05
*
**/
public static String getFileSuffix(String fileName) {
return fileName.substring(fileName.lastIndexOf("."));
}
/**
* 获取文件类型
*
**/
public static String getFileType(InputStream file) {
return FileTypeUtil.getType(file);
}
}

View File

@@ -16,7 +16,9 @@ spring:
shutdown-timeout: 100ms
servlet:
multipart:
max-request-size: 100MB
max-file-size: 100MB
max-request-size: 200MB
resolve-lazily: true
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
@@ -68,3 +70,11 @@ springdoc:
swagger-ui:
enabled: true # 开启swagger界面依赖OpenApi需要OpenApi同时开启
path: /swagger-ui/index.html # 自定义路径,默认为"/swagger-ui/index.html"
minio:
endpoint: http://192.168.1.100:9000
accessKey: minioadmin
secretKey: minioadmin
bucketName: dc-lab-system