Java中的AWS S3文件上传 - 为什么直接上传InputStream比写入临时文件然后上传消耗更多内存?

Tob*_*ran 6 java amazon-s3

我想使用 commons-fileupload 库的 Streaming API 将文件上传到 S3 存储桶。该代码用于解析请求

FileItemIterator iterStream = upload.getItemIterator(request);
while (iterStream.hasNext()) {
    FileItemStream item = iterStream.next();
    String name = item.getFieldName();
    InputStream stream = item.openStream();
    if (!item.isFormField()) {
        // Process the InputStream stream (*)
    } else {
        String formFieldValue = Streams.asString(stream);
    }
}
Run Code Online (Sandbox Code Playgroud)

这个用于初始化 S3 客户端和传输管理器

s3Client = AmazonS3ClientBuilder.standard()
                .withRegion(Regions.DEFAULT_REGION)
                .withCredentials(new ProfileCredentialsProvider())
                .build();
transferManager = TransferManagerBuilder.standard()
                .withS3Client(s3Client)
                .build();
Run Code Online (Sandbox Code Playgroud)

我用了100MB的文件来测试。一开始,我的 springboot 应用程序启动时使用了大约 95MB 的内存。当使用该流(*)上传到 s3 存储桶时,使用

        Upload upload = transferManager.upload(bucketName, key, inputStream, metadata );
Run Code Online (Sandbox Code Playgroud)

与将流 (*) 复制到 OutputStream,然后上传从该输出流创建的文件(从 90MB-> 100MB)相比,内存消耗明显更多(从 90MB->370MB)

try (
      OutputStream out = new FileOutputStream(fileName);
) {
      IOUtils.copy(inputStream, out);
}
PutObjectRequest request = new PutObjectRequest(
                existingBucketName, fileName, new File(fileName));
Upload upload = transferManager.upload(request);

Run Code Online (Sandbox Code Playgroud)

我想知道为什么会这样。inputStream发生了什么导致直接上传消耗更多内存?非常感谢

Xia*_*kun 7

如果您不知道文件的大小,只需使用文件上传即可。应避免使用InputStream 上传。参考这个,

从流上传选项时,调用者必须通过 ObjectMetadata 参数中的内容长度字段提供流中选项的大小。如果没有为输入流指定内容长度,则 TransferManager 将尝试在内存中缓冲所有流内容,并以传统的单部分上传方式上传选项。由于整个流内容必须缓冲在内存中,因此这可能非常昂贵,应尽可能避免。

https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/transfer/TransferManager.html#upload-java.lang.String-java.lang.String-java.io。 InputStream-com.amazonaws.services.s3.model.ObjectMetadata-