Spring Boot 与 Amazon S3:快速上传与下载文件的完整指南
概要
在将 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.6
- spring-cloud-aws-dependencies:3.1.1
- spring-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。
io.awspring.cloud spring-cloud-aws-dependencies 3.1.1 pom import io.awspring.cloud spring-cloud-aws-starter io.awspring.cloud spring-cloud-aws-starter-s3在 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_TIERINGEC2(Server)用
spring: cloud: aws: credentials: instance-profile: true useDefaultAwsCredentialsChain: true stack.auto: false region: instance-profile: true s3: bucket: your-bucket-name storage-class: STANDARDAwsStorageService.java
上传文件使用 MultipartFile 接收,下载时最终会将文件打包成 ResponseEntity 进行返回。
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 resInputStream = s3Client.getObject(getObjRequest);) { return resInputStream.readAllBytes(); } } }官方示例
在解决问题后,我发现了AWS官方提供的示例代码。虽然资料很少,但这份官方样例实际上是AWS SDK for Java的代码示例。
AWS事例
