Java如何实现从第三方存储分块下载大文件并分块上传S3避免内存溢出
实现方案
核心思路是结合流式传输+S3分块上传能力,完全不需要加载整个文件到内存,也可以按照要求实现「下载单块落盘→上传→删除本地块」的逻辑,以下是两种可行的Java实现方式:
方案1:用AWS SDK for Java 2.x 自带的TransferManager实现(推荐)
这个方案不需要手动管理分块逻辑,SDK底层已经封装好了分块、重试、并发控制能力,甚至可以跳过本地临时文件存储,直接把第三方链接的输入流上传到S3,内存占用极低:
- 依赖引入:可直接引入AWS S3 SDK和TransferManager相关依赖包
- 核心代码示例:
// 初始化S3客户端和TransferManager S3Client s3Client = S3Client.builder() .region(Region.of("你的S3区域")) .credentialsProvider(DefaultCredentialsProvider.create()) .build(); TransferManager transferManager = TransferManager.builder() .s3Client(s3Client) // 自定义分块阈值,超过该大小的文件自动走分块上传,默认8MB .multipartUploadThreshold(10 * 1024 * 1024) .build(); // 打开第三方文件链接的输入流 URL thirdPartyFileUrl = new URL("第三方存储提供的文件链接"); HttpURLConnection httpConn = (HttpURLConnection) thirdPartyFileUrl.openConnection(); InputStream fileInputStream = httpConn.getInputStream(); // 直接将流上传到S3,底层自动分块处理,不会加载全量文件到内存 Upload uploadTask = transferManager.upload( PutObjectRequest.builder() .bucket("你的S3桶名") .key("S3上保存的文件路径") .build(), RequestBody.fromInputStream(fileInputStream, httpConn.getContentLengthLong()) ); // 等待上传完成 uploadTask.completionFuture().join(); // 资源清理 fileInputStream.close(); httpConn.disconnect(); transferManager.close(); s3Client.close();
如果必须要走「本地临时存分块→上传→删除」的逻辑,可以在读取输入流时按固定大小写入本地临时文件,上传完成后调用文件删除接口即可。
方案2:手动实现分块上传逻辑(灵活度更高)
如果需要自定义分块规则、错误处理逻辑,可以自行调用S3原生分块上传API实现,步骤如下:
- 第一步:调用
initiateMultipartUpload接口初始化分块上传任务,获取唯一的上传ID - 第二步:打开第三方文件的HTTP输入流,循环读取固定大小的字节(S3要求除最后一块外,单块大小最小为5MB,最大5GB)
- 第三步:每读取一块就写入本地临时文件,调用
uploadPart接口上传该分块,记录返回的ETag和分块编号,上传完成后立刻删除本地临时文件 - 第四步:所有分块上传完成后,调用
completeMultipartUpload接口提交整个上传任务 - 异常兜底:如果任务中途出错,调用
abortMultipartUpload接口取消上传,避免残留未完成的分块占用存储
注意事项
- 可以自行调整分块大小,在带宽足够的情况下适当增大分块可以减少请求次数,提升传输效率
- 第三方文件下载环节建议配置HTTP超时、重试策略,避免网络波动导致任务失败
- 临时文件建议存储在ECS的临时目录(比如
/tmp),要添加异常场景下的临时文件兜底清理逻辑,避免磁盘占用过高 - 可根据ECS带宽配置分块上传并发数,进一步提升传输速度
内容的提问来源于stack exchange,提问作者Mikasa
相关产品推荐
相关产品推荐

