You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用AWS SDK 2.0时,未知InputStream长度如何调用S3 putObject方法

解决AWS SDK 2.x中未知长度InputStream上传S3的问题

我之前刚从AWS SDK 1.x转到2.x时,也遇到过完全一样的困惑!确实2.x的RequestBody.fromInputStream强制要求传入contentLength,不像旧版那样可以直接传流了事。不过针对你1MB-20MB的文件大小,有几个实用的解决方案:

方案1:将InputStream转为字节数组上传(最简单)

这个方案直接把整个流读取到字节数组里,然后用RequestBody.fromBytes上传,代码实现非常简洁:

import org.apache.commons.io.IOUtils; // 可以用Apache Commons IO,也可以自己实现流读取

InputStream stream = otherApi.get();
try {
    byte[] fileBytes = IOUtils.toByteArray(stream);
    s3.putObject(
        PutObjectRequest.builder()
            .bucket("你的存储桶名称")
            .key("文件在S3中的键名")
            .build(),
        RequestBody.fromBytes(fileBytes)
    );
} finally {
    stream.close(); // 记得关闭流
}

优势:代码简单,上手快;20MB的字节数组在现代JVM中不会造成内存压力,完全适配你的场景。
缺点:需要把整个文件加载到内存,如果文件更大(比如超过100MB)就不推荐了。

方案2:先计算流长度再上传

如果不想一次性加载整个文件到内存,可以先读取流计算长度,再重置流进行上传(前提是你的InputStream支持mark()和reset()方法):

InputStream stream = otherApi.get();
try {
    stream.mark(Integer.MAX_VALUE); // 标记当前流位置,确保流支持标记操作
    ByteArrayOutputStream tempOut = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int readLen;
    while ((readLen = stream.read(buffer)) != -1) {
        tempOut.write(buffer, 0, readLen);
    }
    long contentLength = tempOut.size();
    
    stream.reset(); // 重置流到标记的起始位置
    s3.putObject(
        PutObjectRequest.builder()
            .bucket("你的存储桶名称")
            .key("文件在S3中的键名")
            .build(),
        RequestBody.fromInputStream(stream, contentLength)
    );
} finally {
    stream.close();
}

注意:如果你的InputStream不支持mark/reset(比如某些网络流),这个方法会抛出IOException,这时可以考虑下面的临时文件方案。

方案3:用临时文件缓存流后上传

这个方案把InputStream写入本地临时文件,再通过文件上传,内存占用最低,也适合更大的文件:

InputStream stream = otherApi.get();
File tempFile = null;
try {
    tempFile = File.createTempFile("s3-upload-", ".tmp");
    try (FileOutputStream fos = new FileOutputStream(tempFile)) {
        byte[] buffer = new byte[1024];
        int readLen;
        while ((readLen = stream.read(buffer)) != -1) {
            fos.write(buffer, 0, readLen);
        }
    }
    
    s3.putObject(
        PutObjectRequest.builder()
            .bucket("你的存储桶名称")
            .key("文件在S3中的键名")
            .build(),
        RequestBody.fromFile(tempFile)
    );
} finally {
    stream.close();
    if (tempFile != null) {
        tempFile.delete(); // 上传完成后删除临时文件
    }
}

优势:内存占用极低,适合任意大小的文件;即使流不支持mark/reset也能正常工作。
缺点:需要读写本地磁盘,比前两个方案多一点IO开销,但对于20MB的文件来说几乎可以忽略。

为什么SDK 2.x需要contentLength?

其实AWS S3的PUT API本身就要求请求头中携带Content-Length,旧版1.x SDK是在后台帮你悄悄处理了——要么把流缓存到内存/磁盘计算长度,要么自动切换成分块上传。而2.x SDK把这个控制权交给了开发者,让你可以根据自己的场景选择最适合的方式,避免不必要的内存或磁盘开销。

很多刚转2.x的开发者都会遇到这个问题,选个适合你场景的方案就行,你的文件大小用前两个方案都很省心~

内容的提问来源于stack exchange,提问作者mmjan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 14:24:06