从AWS SDK for Java 2.x 调用 AWS 服务
1、JAR包引入
xml
<!-- AWS SDK v2 -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.20.162</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>auth</artifactId>
<version>2.20.162</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
<version>2.20.162</version>
</dependency>
2、配置
yaml
#上传配置
upload:
aws: #aws上传
secretId: AKIA6666
secretKey: daEiuKneDT1111
bucket: live
region: ap-4544-1
rootPath: /crm-drd # 文件存储根路径
days: 7 # 预签名URL有效期(天数)
awsHost: https://45454545-1.amazonaws.com
proxyHost: https://wewewet.ai
enabled : true
3、读取配置
arduino
package com.yicrm.common.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* AWS S3 配置属性
*
* @author yicrm
* @since 2026-01-29
*/
@Data
@Component
@ConfigurationProperties(prefix = "upload.aws")
public class AwsProperties {
/**
* 是否启用 AWS S3
*/
private Boolean enabled = false;
/**
* Access Key ID
*/
private String secretId;
/**
* Secret Access Key
*/
private String secretKey;
/**
* 区域
*/
private String region = "ap-southeast-1";
/**
* 存储桶名称
*/
private String bucket;
/**
* 根路径前缀
*/
private String rootPath = "";
/**
* aws域名
* */
private String awsHost;
/**
* 代理域名(用于替换 S3 URL 的域名)
*/
private String proxyHost;
/**
* 预签名 URL 有效期(天数)
*/
private Integer days = 7;
/**
* 端点 URL(用于兼容 S3 兼容的存储服务)
*/
private String endpoint;
}
4、AwsUtils方法实现
scss
package com.yicrm.common.utils.aws;
import com.yicrm.common.config.AwsProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.time.Duration;
import java.util.UUID;
/**
* AWS S3 工具类
*
* @author yicrm
* @since 2026-01-29
*/
@Slf4j
@Component
public class AwsUtils {
@Autowired(required = false)
private AwsProperties awsProperties;
private S3Client s3Client;
private S3Presigner s3Presigner;
@PostConstruct
public void init() {
if (awsProperties != null && Boolean.TRUE.equals(awsProperties.getEnabled())) {
try {
AwsBasicCredentials awsCreds = AwsBasicCredentials.create(
awsProperties.getSecretId(),
awsProperties.getSecretKey()
);
software.amazon.awssdk.services.s3.S3ClientBuilder s3ClientBuilder = S3Client.builder()
.region(Region.of(awsProperties.getRegion()))
.credentialsProvider(StaticCredentialsProvider.create(awsCreds))
.httpClient(ApacheHttpClient.builder().build());
// 如果配置了自定义端点,使用自定义端点
if (StringUtils.isNotBlank(awsProperties.getEndpoint())) {
s3ClientBuilder.endpointOverride(new URL(awsProperties.getEndpoint()).toURI());
}
s3Client = s3ClientBuilder.build();
S3Presigner.Builder presignerBuilder = S3Presigner.builder()
.region(Region.of(awsProperties.getRegion()))
.credentialsProvider(StaticCredentialsProvider.create(awsCreds));
if (StringUtils.isNotBlank(awsProperties.getEndpoint())) {
presignerBuilder.endpointOverride(new URL(awsProperties.getEndpoint()).toURI());
}
s3Presigner = presignerBuilder.build();
log.info("AWS S3 客户端初始化成功");
} catch (Exception e) {
log.error("AWS S3 客户端初始化失败", e);
}
}
}
@PreDestroy
public void destroy() {
if (s3Client != null) {
s3Client.close();
}
if (s3Presigner != null) {
s3Presigner.close();
}
}
/**
* 检查 AWS 是否启用
*/
public boolean isEnabled() {
return awsProperties != null && Boolean.TRUE.equals(awsProperties.getEnabled()) && s3Client != null;
}
/**
* 上传文件到 S3
*
* @param file 文件
* @return 上传结果
*/
public AwsUploadResult uploadFile(MultipartFile file) {
if (!isEnabled()) {
throw new IllegalStateException("AWS S3 未启用或未正确配置");
}
try {
String originalFilename = file.getOriginalFilename();
String fileExtension = "";
if (originalFilename != null && originalFilename.contains(".")) {
fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
}
String key = UUID.randomUUID().toString().replace("-", "");
String newFileName = key + (StringUtils.isNotBlank(fileExtension) ? "." + fileExtension : "");
// 构建对象键
String rootPath = StringUtils.isNotBlank(awsProperties.getRootPath())
? awsProperties.getRootPath().endsWith("/")
? awsProperties.getRootPath()
: awsProperties.getRootPath() + "/"
: "";
String objectKey = rootPath + newFileName;
// 创建临时文件
File tempFile = File.createTempFile(key, StringUtils.isNotBlank(fileExtension) ? "." + fileExtension : "");
try {
Files.copy(file.getInputStream(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
// 上传到 S3
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(awsProperties.getBucket())
.key(objectKey)
.contentType("application/octet-stream")
.contentDisposition("attachment")
.contentLength(file.getSize())
.build();
s3Client.putObject(putObjectRequest, RequestBody.fromFile(tempFile));
// 生成文件访问 URL
String fileUrl = generatePresignedUrl(objectKey, true);
AwsUploadResult result = new AwsUploadResult();
result.setKey(objectKey);
result.setUrl(fileUrl);
result.setOriginalFilename(originalFilename);
result.setSize(file.getSize());
result.setContentType(file.getContentType());
log.info("AWS S3 文件上传成功: key={}, url={}", objectKey, fileUrl);
return result;
} finally {
// 删除临时文件
if (tempFile.exists()) {
tempFile.delete();
}
}
} catch (Exception e) {
log.error("AWS S3 文件上传失败", e);
throw new RuntimeException("文件上传失败: " + e.getMessage(), e);
}
}
/**
* 上传文件到 S3(使用文件对象)
*
* @param file 文件
* @param objectKey 对象键
* @return 上传结果
*/
public AwsUploadResult uploadFile(File file, String objectKey) {
if (!isEnabled()) {
throw new IllegalStateException("AWS S3 未启用或未正确配置");
}
try {
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(awsProperties.getBucket())
.key(objectKey)
.contentLength(file.length())
.build();
s3Client.putObject(putObjectRequest, RequestBody.fromFile(file));
String fileUrl = generatePresignedUrl(objectKey, true);
AwsUploadResult result = new AwsUploadResult();
result.setKey(objectKey);
result.setUrl(fileUrl);
result.setOriginalFilename(file.getName());
result.setSize(file.length());
log.info("AWS S3 文件上传成功: key={}, url={}", objectKey, fileUrl);
return result;
} catch (Exception e) {
log.error("AWS S3 文件上传失败: key={}", objectKey, e);
throw new RuntimeException("文件上传失败: " + e.getMessage(), e);
}
}
/**
* 下载文件
*
* @param objectKey 对象键
* @param targetPath 目标文件路径
* @return 下载的文件
*/
public File downloadFile(String objectKey, String targetPath) {
if (!isEnabled()) {
throw new IllegalStateException("AWS S3 未启用或未正确配置");
}
try {
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
.bucket(awsProperties.getBucket())
.key(objectKey)
.build();
ResponseInputStream<GetObjectResponse> response = s3Client.getObject(getObjectRequest);
File targetFile = new File(targetPath);
// 确保目标文件的父目录存在
File parentDir = targetFile.getParentFile();
if (parentDir != null && !parentDir.exists()) {
parentDir.mkdirs();
}
// 将响应流写入文件
try (FileOutputStream fos = new FileOutputStream(targetFile);
InputStream inputStream = response) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
log.info("AWS S3 文件下载成功: key={}, path={}", objectKey, targetPath);
return targetFile;
} catch (Exception e) {
log.error("AWS S3 文件下载失败: key={}, path={}", objectKey, targetPath, e);
throw new RuntimeException("文件下载失败: " + e.getMessage(), e);
}
}
/**
* 获取文件输入流
*
* @param objectKey 对象键
* @return 文件输入流
*/
public InputStream getFileInputStream(String objectKey) {
if (!isEnabled()) {
throw new IllegalStateException("AWS S3 未启用或未正确配置");
}
try {
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
.bucket(awsProperties.getBucket())
.key(objectKey)
.build();
ResponseInputStream<GetObjectResponse> response = s3Client.getObject(getObjectRequest);
log.info("AWS S3 文件流获取成功: key={}", objectKey);
return response;
} catch (Exception e) {
log.error("AWS S3 文件流获取失败: key={}", objectKey, e);
throw new RuntimeException("文件流获取失败: " + e.getMessage(), e);
}
}
/**
* 删除文件
*
* @param objectKey 对象键
*/
public void deleteFile(String objectKey) {
if (!isEnabled()) {
throw new IllegalStateException("AWS S3 未启用或未正确配置");
}
try {
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
.bucket(awsProperties.getBucket())
.key(objectKey)
.build();
s3Client.deleteObject(deleteObjectRequest);
log.info("AWS S3 文件删除成功: key={}", objectKey);
} catch (Exception e) {
log.error("AWS S3 文件删除失败: key={}", objectKey, e);
throw new RuntimeException("文件删除失败: " + e.getMessage(), e);
}
}
/**
* 检查文件是否存在
*
* @param objectKey 对象键
* @return 是否存在
*/
public boolean fileExists(String objectKey) {
if (!isEnabled()) {
return false;
}
try {
HeadObjectRequest headObjectRequest = HeadObjectRequest.builder()
.bucket(awsProperties.getBucket())
.key(objectKey)
.build();
s3Client.headObject(headObjectRequest);
return true;
} catch (NoSuchKeyException e) {
return false;
} catch (Exception e) {
log.error("AWS S3 检查文件存在性失败: key={}", objectKey, e);
return false;
}
}
/**
* 生成预签名 URL(用于下载)
*
* @param objectKey 对象键
* @param forceInline 是否强制内联显示(用于预览)
* @return 预签名 URL
*/
public String generatePresignedUrl(String objectKey, boolean forceInline) {
if (!isEnabled()) {
throw new IllegalStateException("AWS S3 未启用或未正确配置");
}
try {
GetObjectRequest.Builder requestBuilder = GetObjectRequest.builder()
.bucket(awsProperties.getBucket())
.key(objectKey);
if (forceInline) {
requestBuilder.responseContentDisposition("inline");
}
GetObjectRequest objectRequest = requestBuilder.build();
GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder()
.signatureDuration(Duration.ofDays(awsProperties.getDays()))
.getObjectRequest(objectRequest)
.build();
PresignedGetObjectRequest presignedRequest = s3Presigner.presignGetObject(presignRequest);
String url = presignedRequest.url().toString();
// 如果配置了代理域名,替换 URL 中的域名
if (StringUtils.isNotBlank(awsProperties.getProxyHost())) {
url = replaceUrlDomain(url, awsProperties.getProxyHost());
}
log.info("生成预签名 URL: key={}, url={}", objectKey, url);
return url;
} catch (Exception e) {
log.error("生成预签名 URL 失败: key={}", objectKey, e);
throw new RuntimeException("生成预签名 URL 失败: " + e.getMessage(), e);
}
}
/**
* 生成预签名下载 URL(用于文件下载)
*
* @param objectKey 对象键
* @return 预签名下载 URL
*/
public String generatePresignedDownloadUrl(String objectKey) {
return generatePresignedDownloadUrl(objectKey, null, null);
}
/**
* 生成预签名下载 URL(用于文件下载,支持自定义文件名)
*
* @param objectKey 对象键
* @param filename 下载时的文件名(可选)
* @return 预签名下载 URL
*/
public String generatePresignedDownloadUrl(String objectKey, String filename) {
return generatePresignedDownloadUrl(objectKey, filename, null);
}
/**
* 生成预签名下载 URL(用于文件下载,支持自定义文件名和有效期)
*
* @param objectKey 对象键
* @param filename 下载时的文件名(可选,如果为空则使用默认文件名)
* @param days 有效期(天数,如果为空则使用配置的默认值)
* @return 预签名下载 URL
*/
public String generatePresignedDownloadUrl(String objectKey, String filename, Integer days) {
if (!isEnabled()) {
throw new IllegalStateException("AWS S3 未启用或未正确配置");
}
try {
GetObjectRequest.Builder requestBuilder = GetObjectRequest.builder()
.bucket(awsProperties.getBucket())
.key(objectKey);
// 如果指定了文件名,设置下载时的文件名
if (StringUtils.isNotBlank(filename)) {
requestBuilder.responseContentDisposition("attachment; filename="" + filename + """);
} else {
// 默认设置为附件下载
requestBuilder.responseContentDisposition("attachment");
}
GetObjectRequest objectRequest = requestBuilder.build();
// 使用指定的有效期或配置的默认值
int signatureDays = days != null ? days : awsProperties.getDays();
GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder()
.signatureDuration(Duration.ofDays(signatureDays))
.getObjectRequest(objectRequest)
.build();
PresignedGetObjectRequest presignedRequest = s3Presigner.presignGetObject(presignRequest);
String url = presignedRequest.url().toString();
// 如果配置了代理域名,替换 URL 中的域名
if (StringUtils.isNotBlank(awsProperties.getProxyHost())) {
url = replaceUrlDomain(url, awsProperties.getProxyHost());
}
log.info("生成预签名下载 URL: key={}, filename={}, days={}, url={}", objectKey, filename, signatureDays, url);
return url;
} catch (Exception e) {
log.error("生成预签名下载 URL 失败: key={}, filename={}", objectKey, filename, e);
throw new RuntimeException("生成预签名下载 URL 失败: " + e.getMessage(), e);
}
}
/**
* 生成预签名上传 URL
*
* @param objectKey 对象键
* @return 预签名上传 URL
*/
public String generatePresignedPutUrl(String objectKey) {
if (!isEnabled()) {
throw new IllegalStateException("AWS S3 未启用或未正确配置");
}
try {
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(awsProperties.getBucket())
.key(objectKey)
.build();
PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofDays(awsProperties.getDays()))
.putObjectRequest(putObjectRequest)
.build();
PresignedPutObjectRequest presignedRequest = s3Presigner.presignPutObject(presignRequest);
String url = presignedRequest.url().toString();
log.info("生成预签名上传 URL: key={}, url={}", objectKey, url);
return url;
} catch (Exception e) {
log.error("生成预签名上传 URL 失败: key={}", objectKey, e);
throw new RuntimeException("生成预签名上传 URL 失败: " + e.getMessage(), e);
}
}
/**
* 替换 URL 中的域名
*
* @param originalUrl 原始 URL
* @param targetDomain 目标域名
* @return 替换后的 URL
*/
private String replaceUrlDomain(String originalUrl, String targetDomain) {
try {
URL url = new URL(originalUrl);
String targetDomainClean = targetDomain.replaceAll("^https?://", "").replaceAll("/$", "");
String protocol = url.getProtocol();
String newUrl = protocol + "://" + targetDomainClean + url.getPath();
if (url.getQuery() != null) {
newUrl += "?" + url.getQuery();
}
return newUrl;
} catch (Exception e) {
log.warn("替换 URL 域名失败: originalUrl={}, targetDomain={}", originalUrl, targetDomain, e);
return originalUrl;
}
}
}
5、文件上传限制
kotlin
file:
upload:
formatList: pdf
size: 52428800
package com.yicrm.common.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 文件上传配置属性
*
* @author yicrm
* @since 2026-01-29
*/
@Data
@Component
@ConfigurationProperties(prefix = "file.upload")
public class FileUploadProperties {
/**
* 允许的文件格式列表(逗号分隔,如:pdf,doc,docx)
*/
private String formatList = "";
/**
* 文件大小限制(字节),默认 50MB
*/
private Long size = 50 * 1024 * 1024L;
}
6、AWS实现类
kotlin
package com.yicrm.biz.service.business;
import com.yicrm.common.config.FileUploadProperties;
import com.yicrm.common.core.domain.AjaxResult;
import com.yicrm.common.utils.aws.AwsUploadResult;
import com.yicrm.common.utils.aws.AwsUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 文件上传下载业务服务
*
* @author yicrm
* @since 2026-01-29
*/
@Slf4j
@Service
public class FileUploadBusinessService {
@Resource
private AwsUtils awsUtils;
@Resource
private FileUploadProperties fileUploadProperties;
/**
* AWS S3 文件上传(单个)
*
* @param file 文件
* @return 上传结果
*/
public AjaxResult uploadFile(MultipartFile file) {
try {
if (file == null || file.isEmpty()) {
return AjaxResult.error("文件不能为空");
}
// 验证文件格式和大小
AjaxResult validationResult = validateFile(file);
if (validationResult != null) {
return validationResult;
}
if (!awsUtils.isEnabled()) {
return AjaxResult.error("AWS S3 未启用或未正确配置");
}
AwsUploadResult result = awsUtils.uploadFile(file);
Map<String, Object> data = new HashMap<>();
data.put("key", result.getKey());
data.put("url", result.getUrl());
data.put("fileName", result.getKey());
data.put("originalFilename", result.getOriginalFilename());
data.put("size", result.getSize());
data.put("contentType", result.getContentType());
// data.put("contentType", "application/octet-stream");
// data.put("Content-Disposition", "attachment; filename="+result.getOriginalFilename());
return AjaxResult.success("文件上传成功", data);
} catch (Exception e) {
log.error("AWS S3 文件上传失败", e);
return AjaxResult.error("文件上传失败: " + e.getMessage());
}
}
/**
* AWS S3 文件上传(多个)
*
* @param files 文件列表
* @return 上传结果
*/
public AjaxResult uploadFiles(List<MultipartFile> files) {
try {
if (files == null || files.isEmpty()) {
return AjaxResult.error("文件列表不能为空");
}
if (!awsUtils.isEnabled()) {
return AjaxResult.error("AWS S3 未启用或未正确配置");
}
List<Map<String, Object>> results = new ArrayList<>();
List<String> keys = new ArrayList<>();
List<String> urls = new ArrayList<>();
List<String> fileNames = new ArrayList<>();
List<String> originalFilenames = new ArrayList<>();
for (MultipartFile file : files) {
if (file.isEmpty()) {
continue;
}
// 验证文件格式和大小
AjaxResult validationResult = validateFile(file);
if (validationResult != null) {
// 如果验证失败,返回错误信息
return validationResult;
}
AwsUploadResult result = awsUtils.uploadFile(file);
Map<String, Object> data = new HashMap<>();
data.put("key", result.getKey());
data.put("url", result.getUrl());
data.put("fileName", result.getKey());
data.put("originalFilename", result.getOriginalFilename());
data.put("size", result.getSize());
data.put("contentType", result.getContentType());
results.add(data);
keys.add(result.getKey());
urls.add(result.getUrl());
fileNames.add(result.getKey());
originalFilenames.add(result.getOriginalFilename());
}
Map<String, Object> responseData = new HashMap<>();
responseData.put("results", results);
responseData.put("keys", StringUtils.join(keys, ","));
responseData.put("urls", StringUtils.join(urls, ","));
responseData.put("fileNames", StringUtils.join(fileNames, ","));
responseData.put("originalFilenames", StringUtils.join(originalFilenames, ","));
return AjaxResult.success("文件上传成功", responseData);
} catch (Exception e) {
log.error("AWS S3 批量文件上传失败", e);
return AjaxResult.error("文件上传失败: " + e.getMessage());
}
}
/**
* 生成 AWS S3 预签名下载 URL
*
* @param key 对象键(S3 中的文件路径)
* @param filename 下载时的文件名(可选)
* @param days 有效期(天数,可选,默认使用配置值)
* @return 预签名下载 URL
*/
public AjaxResult generateDownloadUrl(String key, String filename, Integer days) {
try {
if (StringUtils.isBlank(key)) {
return AjaxResult.error("对象键不能为空");
}
if (!awsUtils.isEnabled()) {
return AjaxResult.error("AWS S3 未启用或未正确配置");
}
String downloadUrl;
if (filename != null && days != null) {
downloadUrl = awsUtils.generatePresignedDownloadUrl(key, filename, days);
} else if (filename != null) {
downloadUrl = awsUtils.generatePresignedDownloadUrl(key, filename);
} else {
downloadUrl = awsUtils.generatePresignedDownloadUrl(key);
}
Map<String, Object> data = new HashMap<>();
data.put("url", downloadUrl);
data.put("key", key);
data.put("filename", filename);
return AjaxResult.success("生成下载 URL 成功", data);
} catch (Exception e) {
log.error("生成 AWS S3 下载 URL 失败: key={}", key, e);
return AjaxResult.error("生成下载 URL 失败: " + e.getMessage());
}
}
/**
* 生成 AWS S3 预签名预览 URL
*
* @param key 对象键(S3 中的文件路径)
* @return 预签名预览 URL
*/
public AjaxResult generatePreviewUrl(String key) {
try {
if (StringUtils.isBlank(key)) {
return AjaxResult.error("对象键不能为空");
}
if (!awsUtils.isEnabled()) {
return AjaxResult.error("AWS S3 未启用或未正确配置");
}
String previewUrl = awsUtils.generatePresignedUrl(key, true);
Map<String, Object> data = new HashMap<>();
data.put("url", previewUrl);
data.put("key", key);
return AjaxResult.success("生成预览 URL 成功", data);
} catch (Exception e) {
log.error("生成 AWS S3 预览 URL 失败: key={}", key, e);
return AjaxResult.error("生成预览 URL 失败: " + e.getMessage());
}
}
/**
* 删除 AWS S3 文件
*
* @param key 对象键(S3 中的文件路径)
* @return 删除结果
*/
public AjaxResult deleteFile(String key) {
try {
if (StringUtils.isBlank(key)) {
return AjaxResult.error("对象键不能为空");
}
if (!awsUtils.isEnabled()) {
return AjaxResult.error("AWS S3 未启用或未正确配置");
}
awsUtils.deleteFile(key);
return AjaxResult.success("文件删除成功");
} catch (Exception e) {
log.error("删除 AWS S3 文件失败: key={}", key, e);
return AjaxResult.error("文件删除失败: " + e.getMessage());
}
}
/**
* 检查 AWS S3 文件是否存在
*
* @param key 对象键(S3 中的文件路径)
* @return 是否存在
*/
public AjaxResult checkFileExists(String key) {
try {
if (StringUtils.isBlank(key)) {
return AjaxResult.error("对象键不能为空");
}
if (!awsUtils.isEnabled()) {
return AjaxResult.error("AWS S3 未启用或未正确配置");
}
boolean exists = awsUtils.fileExists(key);
Map<String, Object> data = new HashMap<>();
data.put("exists", exists);
data.put("key", key);
return AjaxResult.success("检查完成", data);
} catch (Exception e) {
log.error("检查 AWS S3 文件存在性失败: key={}", key, e);
return AjaxResult.error("检查失败: " + e.getMessage());
}
}
/**
* 验证文件格式和大小
*
* @param file 文件
* @return 验证失败返回错误结果,验证成功返回 null
*/
private AjaxResult validateFile(MultipartFile file) {
// 验证文件大小
if (fileUploadProperties.getSize() != null && file.getSize() > fileUploadProperties.getSize()) {
long maxSizeMB = fileUploadProperties.getSize() / 1024 / 1024;
return AjaxResult.error(String.format("文件大小超出限制,允许的最大文件大小为:%d MB", maxSizeMB));
}
// 验证文件格式
if (StringUtils.isNotBlank(fileUploadProperties.getFormatList())) {
String originalFilename = file.getOriginalFilename();
if (StringUtils.isBlank(originalFilename)) {
return AjaxResult.error("文件名不能为空");
}
// 获取文件扩展名
String extension = getFileExtension(originalFilename);
if (StringUtils.isBlank(extension)) {
return AjaxResult.error("文件格式不支持,无法识别文件类型");
}
// 解析允许的格式列表
String[] allowedFormats = fileUploadProperties.getFormatList().split(",");
List<String> allowedFormatList = new ArrayList<>();
for (String format : allowedFormats) {
String trimmedFormat = format.trim().toLowerCase();
if (StringUtils.isNotBlank(trimmedFormat)) {
allowedFormatList.add(trimmedFormat);
}
}
// 检查文件格式是否在允许列表中
if (!allowedFormatList.isEmpty() && !allowedFormatList.contains(extension.toLowerCase())) {
return AjaxResult.error(String.format("文件格式不支持,允许的格式为:%s,当前文件格式:%s",
fileUploadProperties.getFormatList(), extension));
}
}
return null; // 验证通过
}
/**
* 获取文件扩展名
*
* @param filename 文件名
* @return 扩展名(不包含点)
*/
private String getFileExtension(String filename) {
if (StringUtils.isBlank(filename)) {
return "";
}
int lastDotIndex = filename.lastIndexOf('.');
if (lastDotIndex > 0 && lastDotIndex < filename.length() - 1) {
return filename.substring(lastDotIndex + 1);
}
return "";
}
}
7、controller实现
less
package com.yicrm.biz.controller;
import com.yicrm.biz.service.business.FileUploadBusinessService;
import com.yicrm.common.core.domain.AjaxResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.util.List;
/**
* 文件上传下载控制器
*
* @author yicrm
* @since 2026-01-29
*/
@RestController
@RequestMapping("/file")
@Tag(name = "文件上传下载")
public class FileUploadController {
@Resource
private FileUploadBusinessService fileUploadBusinessService;
/**
* AWS S3 文件上传(单个)
*
* @param file 文件
* @return 上传结果
*/
@Operation(summary = "AWS S3 文件上传(单个)", description = "上传单个文件到 AWS S3")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "上传成功",
content = @Content(schema = @Schema(implementation = AjaxResult.class))),
@ApiResponse(responseCode = "500", description = "上传失败")
})
@PostMapping("/aws/upload")
// @PreAuthorize("@ss.hasPermi('file:upload')")
public AjaxResult uploadFileToAws(@RequestParam("file") MultipartFile file) {
return fileUploadBusinessService.uploadFile(file);
}
/**
* AWS S3 文件上传(多个)
*
* @param files 文件列表
* @return 上传结果
*/
@Operation(summary = "AWS S3 文件上传(多个)", description = "批量上传多个文件到 AWS S3")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "上传成功",
content = @Content(schema = @Schema(implementation = AjaxResult.class))),
@ApiResponse(responseCode = "500", description = "上传失败")
})
@PostMapping("/aws/uploads")
// @PreAuthorize("@ss.hasPermi('file:upload')")
public AjaxResult uploadFilesToAws(@RequestParam("files") List<MultipartFile> files) {
return fileUploadBusinessService.uploadFiles(files);
}
/**
* 生成 AWS S3 预签名下载 URL
*
* @param key 对象键(S3 中的文件路径)
* @param filename 下载时的文件名(可选)
* @param days 有效期(天数,可选,默认使用配置值)
* @return 预签名下载 URL
*/
@Operation(summary = "生成 AWS S3 预签名下载 URL", description = "生成用于文件下载的预签名 URL")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "生成成功",
content = @Content(schema = @Schema(implementation = AjaxResult.class))),
@ApiResponse(responseCode = "500", description = "生成失败")
})
@GetMapping("/aws/download/url")
// @PreAuthorize("@ss.hasPermi('file:download')")
public AjaxResult generateDownloadUrl(
@RequestParam("key") String key,
@RequestParam(value = "filename", required = false) String filename,
@RequestParam(value = "days", required = false) Integer days) {
return fileUploadBusinessService.generateDownloadUrl(key, filename, days);
}
/**
* 生成 AWS S3 预签名预览 URL
*
* @param key 对象键(S3 中的文件路径)
* @return 预签名预览 URL
*/
@Operation(summary = "生成 AWS S3 预签名预览 URL", description = "生成用于文件预览的预签名 URL")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "生成成功",
content = @Content(schema = @Schema(implementation = AjaxResult.class))),
@ApiResponse(responseCode = "500", description = "生成失败")
})
@GetMapping("/aws/preview/url")
// @PreAuthorize("@ss.hasPermi('file:preview')")
public AjaxResult generatePreviewUrl(@RequestParam("key") String key) {
return fileUploadBusinessService.generatePreviewUrl(key);
}
/**
* 删除 AWS S3 文件
*
* @param key 对象键(S3 中的文件路径)
* @return 删除结果
*/
@Operation(summary = "删除 AWS S3 文件", description = "从 AWS S3 删除指定文件")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "删除成功",
content = @Content(schema = @Schema(implementation = AjaxResult.class))),
@ApiResponse(responseCode = "500", description = "删除失败")
})
@DeleteMapping("/aws/delete")
// @PreAuthorize("@ss.hasPermi('file:delete')")
public AjaxResult deleteFileFromAws(@RequestParam("key") String key) {
return fileUploadBusinessService.deleteFile(key);
}
/**
* 检查 AWS S3 文件是否存在
*
* @param key 对象键(S3 中的文件路径)
* @return 是否存在
*/
@Operation(summary = "检查 AWS S3 文件是否存在", description = "检查指定文件是否存在于 AWS S3")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "检查完成",
content = @Content(schema = @Schema(implementation = AjaxResult.class))),
@ApiResponse(responseCode = "500", description = "检查失败")
})
@GetMapping("/aws/exists")
// @PreAuthorize("@ss.hasPermi('file:query')")
public AjaxResult checkFileExists(@RequestParam("key") String key) {
return fileUploadBusinessService.checkFileExists(key);
}
}