diff --git a/pom.xml b/pom.xml
index 4374986..7611098 100644
--- a/pom.xml
+++ b/pom.xml
@@ -95,16 +95,28 @@
spring-restdocs-mockmvc
test
+
org.springdoc
springdoc-openapi-starter-webmvc-ui
- 2.3.0
+ 2.6.0
+
org.xerial
sqlite-jdbc
test
+
+ com.amazonaws
+ aws-java-sdk-s3
+ 1.12.706
+
+
+ commons-io
+ commons-io
+ 2.11.0
+
diff --git a/src/main/java/top/suyiiyii/sims/common/S3Config.java b/src/main/java/top/suyiiyii/sims/common/S3Config.java
new file mode 100644
index 0000000..900618a
--- /dev/null
+++ b/src/main/java/top/suyiiyii/sims/common/S3Config.java
@@ -0,0 +1,17 @@
+package top.suyiiyii.sims.common;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import top.suyiiyii.sims.utils.S3Client;
+
+@Configuration
+public class S3Config {
+ @Bean
+ public static S3Client Config(@Value("${S3.ENDPOINT}") String endpoint,
+ @Value("${S3.ACCESS_KEY}") String accessKey,
+ @Value("${S3.SECRET_KEY}") String secretKey,
+ @Value("${S3.BUCKET}") String bucket) {
+ return new S3Client(endpoint, accessKey, secretKey, bucket);
+ }
+}
diff --git a/src/main/java/top/suyiiyii/sims/controller/FileController.java b/src/main/java/top/suyiiyii/sims/controller/FileController.java
new file mode 100644
index 0000000..d66f458
--- /dev/null
+++ b/src/main/java/top/suyiiyii/sims/controller/FileController.java
@@ -0,0 +1,40 @@
+package top.suyiiyii.sims.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import top.suyiiyii.sims.common.AuthAccess;
+import top.suyiiyii.sims.service.FileService;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+@Slf4j
+@RestController
+public class FileController {
+ @Autowired
+ FileService fileService;
+
+ @AuthAccess(allowRoles = {"user"})
+ @Operation(summary = "上传文件", description = "使用form-data格式\nfile:文件\nfilename: 文件名\n返回可访问的路径")
+ @PostMapping("/upload")
+ public String uploadFile(
+ @Parameter String filename,
+ @RequestBody(content = @Content(mediaType = "multipart/form-data",
+ schema = @Schema(type = "string", format = "binary"))) MultipartFile file) {
+ try (InputStream in = file.getInputStream()) {
+ log.info("文件上传,文件名:{},描述:{}", file.getOriginalFilename(), filename);
+ return fileService.uploadFile(in, filename);
+ } catch (IOException e) {
+ log.warn("文件上传失败", e);
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/top/suyiiyii/sims/service/FileService.java b/src/main/java/top/suyiiyii/sims/service/FileService.java
new file mode 100644
index 0000000..f632a56
--- /dev/null
+++ b/src/main/java/top/suyiiyii/sims/service/FileService.java
@@ -0,0 +1,18 @@
+package top.suyiiyii.sims.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import top.suyiiyii.sims.utils.S3Client;
+
+import java.io.InputStream;
+
+@Service
+public class FileService {
+ @Autowired
+ private S3Client s3Client;
+
+ public String uploadFile(InputStream input,String fileName) {
+ String extension = fileName.substring(fileName.lastIndexOf("."));
+ return s3Client.uploadFile(input, extension);
+ }
+}
diff --git a/src/main/java/top/suyiiyii/sims/utils/S3Client.java b/src/main/java/top/suyiiyii/sims/utils/S3Client.java
new file mode 100644
index 0000000..837f121
--- /dev/null
+++ b/src/main/java/top/suyiiyii/sims/utils/S3Client.java
@@ -0,0 +1,123 @@
+package top.suyiiyii.sims.utils;
+
+
+import com.amazonaws.ClientConfiguration;
+import com.amazonaws.Protocol;
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.AmazonS3Client;
+import com.amazonaws.services.s3.S3ClientOptions;
+import com.amazonaws.services.s3.model.ObjectMetadata;
+import com.amazonaws.services.s3.model.S3Object;
+import org.apache.commons.io.IOUtils;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.UUID;
+
+
+public class S3Client {
+ private final String endpoint;
+ private final String bucket;
+ private final AmazonS3 s3client;
+
+ public S3Client(String endpoint, String accessKey, String secretKey, String bucket) {
+ this.endpoint = endpoint;
+ this.bucket = bucket;
+ URL endpointUrl;
+ try {
+ endpointUrl = new URL(endpoint);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ String protocol = endpointUrl.getProtocol();
+ int port = endpointUrl.getPort() == -1 ? endpointUrl.getDefaultPort() : endpointUrl.getPort();
+ ClientConfiguration clientConfig = new ClientConfiguration();
+ clientConfig.setSignerOverride("S3SignerType");
+ clientConfig.setProtocol(Protocol.valueOf(protocol.toUpperCase()));
+ // 禁用证书检查,避免https自签证书校验失败
+ System.setProperty("com.amazonaws.sdk.disableCertChecking", "true");
+ // 屏蔽 AWS 的 MD5 校验,避免校验导致的下载抛出异常问题
+ System.setProperty("com.amazonaws.services.s3.disableGetObjectMD5Validation", "true");
+ AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
+ // 创建 S3Client 实例
+ AmazonS3 s3client = new AmazonS3Client(awsCredentials, clientConfig);
+ s3client.setEndpoint(endpointUrl.getHost() + ":" + port);
+ s3client.setS3ClientOptions(S3ClientOptions.builder().setPathStyleAccess(true).build());
+ this.s3client = s3client;
+ }
+
+
+ public boolean bucketExists(String bucket) {
+ try {
+ return s3client.doesBucketExist(bucket);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ public boolean existObject(String bucket, String objectId) {
+ try {
+ return s3client.doesObjectExist(bucket, objectId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public InputStream download(String bucket, String objectId) {
+ try {
+ S3Object o = s3client.getObject(bucket, objectId);
+ return o.getObjectContent();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public void download(String bucket, String objectId, OutputStream out) {
+ S3Object o = s3client.getObject(bucket, objectId);
+ try (InputStream in = o.getObjectContent()) {
+ IOUtils.copyLarge(in, out);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void upload(String bucket, String objectId, InputStream input) {
+ try {
+ // 创建文件上传的元数据
+ ObjectMetadata meta = new ObjectMetadata();
+ // 设置文件上传长度
+ meta.setContentLength(input.available());
+ // 上传
+ s3client.putObject(bucket, objectId, input, meta);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public String uploadFile(InputStream input) {
+ String objectID = UUID.randomUUID().toString();
+ upload(bucket, objectID, input);
+ return endpoint + "/" + bucket + "/" + objectID;
+ }
+
+ /**
+ * 接收文件流,自动使用随机uuid命名并保留扩展名
+ * 返回公网上可以直接访问的URL
+ *
+ * @param input 文件流
+ * @param extensionName 扩展名
+ * @return 文件的URL
+ */
+ public String uploadFile(InputStream input, String extensionName) {
+ String objectID = UUID.randomUUID() + extensionName;
+ upload(bucket, objectID, input);
+ return endpoint + "/" + bucket + "/" + objectID;
+ }
+}
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 4b7599a..17c0cca 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -2,6 +2,10 @@
spring:
profiles:
active: prod
+ servlet:
+ multipart:
+ max-file-size: 100MB
+ max-request-size: 100MB
datasource:
url: ${DATASOURCE_URL}
username: ${DATASOURCE_USERNAME}
@@ -14,4 +18,13 @@ auto-table:
model-package: top.suyiiyii.sims.entity
jwt:
- secret: ${JWT_SECRET}
\ No newline at end of file
+ secret: ${JWT_SECRET}
+
+S3:
+ ENDPOINT: ${S3_ENDPOINT}
+ BUCKET: ${S3_BUCKET}
+ ACCESS_KEY: ${S3_ACCESS_KEY}
+ SECRET_KEY: ${S3_SECRET_KEY}
+
+springdoc:
+ default-support-form-data: true
\ No newline at end of file