diff --git a/pom.xml b/pom.xml
index bef4fa3..4444dd4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -131,6 +131,12 @@
fastjson2
${fastjson2.version}
+
+
+ io.minio
+ minio
+ 8.6.0
+
diff --git a/src/main/java/com/dc/dc_project/config/MinioConfig.java b/src/main/java/com/dc/dc_project/config/MinioConfig.java
new file mode 100644
index 0000000..33a75f0
--- /dev/null
+++ b/src/main/java/com/dc/dc_project/config/MinioConfig.java
@@ -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();
+ }
+}
diff --git a/src/main/java/com/dc/dc_project/controller/PositionController.java b/src/main/java/com/dc/dc_project/controller/PositionController.java
index 745f5eb..ac7f8d6 100644
--- a/src/main/java/com/dc/dc_project/controller/PositionController.java
+++ b/src/main/java/com/dc/dc_project/controller/PositionController.java
@@ -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;
diff --git a/src/main/java/com/dc/dc_project/controller/sys/FileController.java b/src/main/java/com/dc/dc_project/controller/sys/FileController.java
new file mode 100644
index 0000000..fb1769a
--- /dev/null
+++ b/src/main/java/com/dc/dc_project/controller/sys/FileController.java
@@ -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);
+ }
+}
diff --git a/src/main/java/com/dc/dc_project/enums/FileType.java b/src/main/java/com/dc/dc_project/enums/FileType.java
new file mode 100644
index 0000000..89228a8
--- /dev/null
+++ b/src/main/java/com/dc/dc_project/enums/FileType.java
@@ -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;
+ }
+}
diff --git a/src/main/java/com/dc/dc_project/model/dto/FileUploadDto.java b/src/main/java/com/dc/dc_project/model/dto/FileUploadDto.java
new file mode 100644
index 0000000..114c7e7
--- /dev/null
+++ b/src/main/java/com/dc/dc_project/model/dto/FileUploadDto.java
@@ -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 files;
+
+ private String remark;
+}
diff --git a/src/main/java/com/dc/dc_project/service/FileService.java b/src/main/java/com/dc/dc_project/service/FileService.java
index 4a2d781..3809727 100644
--- a/src/main/java/com/dc/dc_project/service/FileService.java
+++ b/src/main/java/com/dc/dc_project/service/FileService.java
@@ -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 {
+ /**
+ * 文件上传
+ * @param fileUploadDto
+ * @param userId
+ * @return
+ */
+ ResponseResult upload(FileUploadDto fileUploadDto, Long userId);
}
diff --git a/src/main/java/com/dc/dc_project/service/impl/FileServiceImpl.java b/src/main/java/com/dc/dc_project/service/impl/FileServiceImpl.java
index abc644b..2560c2b 100644
--- a/src/main/java/com/dc/dc_project/service/impl/FileServiceImpl.java
+++ b/src/main/java/com/dc/dc_project/service/impl/FileServiceImpl.java
@@ -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
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 uploadedUrls = new ArrayList<>();
+ List 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());
+ }
+ }
}
diff --git a/src/main/java/com/dc/dc_project/service/impl/MinioService.java b/src/main/java/com/dc/dc_project/service/impl/MinioService.java
new file mode 100644
index 0000000..198887e
--- /dev/null
+++ b/src/main/java/com/dc/dc_project/service/impl/MinioService.java
@@ -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 reqParams = new HashMap();
+ 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);
+ }
+ }
+}
diff --git a/src/main/java/com/dc/dc_project/service/PositionServiceImpl.java b/src/main/java/com/dc/dc_project/service/impl/PositionServiceImpl.java
similarity index 97%
rename from src/main/java/com/dc/dc_project/service/PositionServiceImpl.java
rename to src/main/java/com/dc/dc_project/service/impl/PositionServiceImpl.java
index d89fd7e..1b3791c 100644
--- a/src/main/java/com/dc/dc_project/service/PositionServiceImpl.java
+++ b/src/main/java/com/dc/dc_project/service/impl/PositionServiceImpl.java
@@ -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
- implements PositionService{
+ implements PositionService {
private final OrgMapper orgMapper;
private final PersonnelPositionMapper personnelPositionMapper;
diff --git a/src/main/java/com/dc/dc_project/utils/FileUtil.java b/src/main/java/com/dc/dc_project/utils/FileUtil.java
new file mode 100644
index 0000000..e646a96
--- /dev/null
+++ b/src/main/java/com/dc/dc_project/utils/FileUtil.java
@@ -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);
+ }
+
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 4f0ac28..1535ba6 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -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
+
+