如何通过预签名URL将S3文件转存至另一存储桶(Java Spring Boot)
实现S3预签名URL直接转存到目标S3存储桶(Spring Boot方案)
核心思路
S3本身不支持通过第三方预签名URL直接跨桶复制,所以我们需要在Spring Boot应用中做中转:通过预签名URL获取源文件的输入流,直接将流上传到目标S3桶,全程不落地本地文件,避免额外的磁盘IO开销。
步骤1:引入依赖
如果使用AWS SDK v2(推荐版本),在pom.xml中添加以下依赖:
<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> <version>2.20.100</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
步骤2:配置S3客户端
创建配置类初始化用于操作目标存储桶的S3客户端:
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class S3Config { @Bean public S3Client s3Client() { // 替换为目标存储桶所在的AWS区域 Region region = Region.US_EAST_1; return S3Client.builder() .region(region) .credentialsProvider(DefaultCredentialsProvider.create()) .build(); } }
注意:确保应用运行环境拥有访问目标S3桶的权限(比如通过IAM角色、环境变量配置的密钥等方式)。
步骤3:实现转存服务
编写核心转存逻辑,直接通过流传递数据,避免本地存储:
import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import org.springframework.stereotype.Service; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; @Service public class S3TransferService { private final S3Client s3Client; public S3TransferService(S3Client s3Client) { this.s3Client = s3Client; } public void transferViaPresignedUrl(String sourcePresignedUrl, String targetBucket, String targetKey) throws Exception { // 1. 通过预签名URL获取源文件输入流 URL url = new URL(sourcePresignedUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); try (InputStream inputStream = connection.getInputStream()) { // 2. 直接将流上传到目标S3桶 PutObjectRequest putRequest = PutObjectRequest.builder() .bucket(targetBucket) .key(targetKey) .build(); s3Client.putObject(putRequest, software.amazon.awssdk.core.sync.RequestBody.fromInputStream(inputStream, connection.getContentLengthLong())); } finally { connection.disconnect(); } } }
步骤4:接口调用示例
如果需要对外提供转存接口,可以编写一个简单的Controller:
import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class TransferController { private final S3TransferService transferService; public TransferController(S3TransferService transferService) { this.transferService = transferService; } @PostMapping("/transfer-s3") public ResponseEntity<String> transferFile( @RequestParam String sourcePresignedUrl, @RequestParam String targetBucket, @RequestParam String targetKey) { try { transferService.transferViaPresignedUrl(sourcePresignedUrl, targetBucket, targetKey); return ResponseEntity.ok("文件转存成功"); } catch (Exception e) { return ResponseEntity.internalServerError().body("转存失败:" + e.getMessage()); } } }
关键注意事项
- 预签名URL权限:确保源桶的预签名URL拥有
GET权限,且未超过有效期。 - 流资源管理:必须使用try-with-resources语法自动关闭输入流,避免资源泄漏。
- 异常细化处理:生产环境中需针对连接超时、S3权限不足、预签名URL无效等场景单独捕获处理。
- 大文件优化:如果处理超大文件,建议使用S3的分段上传(Multipart Upload)功能,避免内存溢出。
内容的提问来源于stack exchange,提问作者Tejaswini Jadhav
相关产品推荐
相关产品推荐

