添加文件上传功能及S3集成

新版本中添加了文件上传功能,并集成了S3进行文件存储。这包括控制器、服务和工具类的更新,以及配置S3客户端的bean配置。
This commit is contained in:
suyiiyii 2024-09-03 16:13:00 +08:00
parent f113c341e5
commit b9ca333958
6 changed files with 225 additions and 2 deletions

14
pom.xml
View File

@ -95,16 +95,28 @@
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.12.706</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
<build>

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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}
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