概要
在将 Spring Boot 更新到 3 系列时,由于 javax
需要被替换为 jakarta
,因此原先依赖于 javax
的 spring-cloud-starter-aws1
将无法使用(虽然在我本地环境中仍然可以正常工作)。为了确保兼容性,我将依赖关系更改为 jakarta
的 io.awspring.cloud.spring-cloud-aws-starter
,但由于信息较少,特此发布一个示例。
环境
- Java 17
- Spring Boot
spring-boot-starter-parent
:3.2.6spring-cloud-aws-dependencies
:3.1.1spring-cloud-aws-starter
spring-cloud-aws-starter-s3
示例
以下示例展示了如何将文件(对象)上传到 Amazon S3,并指定存储类为 Intelligent-Tiering。假设从本地环境上传时使用 Intelligent-Tiering,而在 EC2(服务器)环境中上传时使用 Standard(即不指定存储类时的默认值)。
※ 直接相关的部分将被省略。
pom.xml
虽然直接使用 AWS SDK For Java 也是一种选择,但本示例中我们将使用 spring-cloud-aws-starter
。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-dependencies</artifactId>
<version>3.1.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-starter</artifactId>
</dependency>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-starter-s3</artifactId>
</dependency>
</dependencies>
在 dependencyManagement
中指定 spring-cloud-aws-dependencies
可以统一管理版本。
AwsStorageConfig.java
为了在 Service
类中通过 Autowired
使用 S3Client
,需要创建一个配置类并将其注册为 Bean。在 EC2 上可以通过 IAM Role 获取认证信息,但在本地环境中无法获取,因此需要显式指定认证信息。
package com.tamorieeeen.sample.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
/**
*
* @author rralucard
*
*/
@Configuration
public class AwsStorageConfig {
@Value("${spring.cloud.aws.credentials.access-key:unknown}")
private String accessKey;
@Value("${spring.cloud.aws.credentials.secret-key:unknown}")
private String secretKey;
@Value("${spring.cloud.aws.region.static:unknown}")
private String region;
/**
* local用
*/
@Bean("s3Client")
@Profile("local")
public S3Client s3ClientLocal() {
StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
AwsBasicCredentials.create(accessKey, secretKey));
return S3Client.builder()
.region(Region.of(region))
.credentialsProvider(credentialsProvider)
.build();
}
/**
* EC2(server)用
*/
@Bean("s3Client")
@Profile("server")
public S3Client s3Client() {
return S3Client.create();
}
}
application.yml
本地使用的 application-local.yml
和 EC2(服务器)使用的 application-server.yml
的配置示例。
本地环境:
spring:
cloud:
aws:
credentials:
instance-profile: false
access-key: SUMPLEACCESSKEY1234
secret-key: SumpleSecretKey123456789
stack.auto: false
region:
instance-profile: false
static: ap-northeast-1
s3:
bucket: your-bucket-name
storage-class: INTELLIGENT_TIERING
EC2(Server)用
spring:
cloud:
aws:
credentials:
instance-profile: true
useDefaultAwsCredentialsChain: true
stack.auto: false
region:
instance-profile: true
s3:
bucket: your-bucket-name
storage-class: STANDARD
AwsStorageService.java
上传文件使用 MultipartFile
接收,下载时最终会将文件打包成 ResponseEntity<byte[]>
进行返回。
package com.tamorieeeen.sample.service;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
/**
*
* @author rralucard
*
*/
@Service
public class AwsStorageService {
@Value("${spring.cloud.aws.s3.bucket}")
private String bucket;
@Value("${spring.cloud.aws.s3.storage-class}")
private String storageClass;
@Autowired
private S3Client s3Client;
/**
* 向s3上传文件
* @throws IOException
* @throws SdkClientException
*/
public void uploadFile(String s3Path, MultipartFile file) throws SdkClientException, IOException {
PutObjectRequest putObjRequest = PutObjectRequest.builder()
.bucket(bucket)
.key(s3Path)
.storageClass(storageClass)
.contentType(file.getContentType())
.contentLength(file.getSize())
.build();
byte[] bytes = file.getBytes();
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);) {
s3Client.putObject(putObjRequest, RequestBody.fromInputStream(inputStream, bytes.length));
}
}
/**
* 从S3下载文件
* @throws IOException
*/
public byte[] download(String s3Path) throws IOException {
GetObjectRequest getObjRequest = GetObjectRequest.builder()
.bucket(bucket)
.key(s3Path)
.build();
try (ResponseInputStream<GetObjectResponse> resInputStream = s3Client.getObject(getObjRequest);) {
return resInputStream.readAllBytes();
}
}
}
官方示例
在解决问题后,我发现了AWS官方提供的示例代码。虽然资料很少,但这份官方样例实际上是AWS SDK for Java的代码示例。