如何设置InputStream内容长度

How to set InputStream content Length

我正在将文件上传到 Amazon S3 存储桶。正在上传文件,但我收到以下警告。

WARNING: No content length specified for stream data. Stream contents will be buffered in memory and could result in out of memory errors.

所以我在我的代码中添加了以下行

metaData.setContentLength(IOUtils.toByteArray(input).length);

但后来我收到了以下消息。我什至不知道这是警告还是什么。

Data read has a different length than the expected: dataLength=0; expectedLength=111992; includeSkipped=false; in.getClass()=class sun.net.httpserver.FixedLengthInputStream; markedSupported=false; marked=0; resetSinceLastMarked=false; markCount=0; resetCount=0

如何将 contentLength 设置为 InputSteam 的元数据?任何帮助将不胜感激。

当您使用 IOUtils.toByteArray 读取数据时,这会消耗 InputStream。当 AWS API 尝试读取它时,它的长度为零。

将内容读入一个字节数组并提供一个 InputStream 将该数组包装到 API:

byte[] bytes = IOUtils.toByteArray(input);
metaData.setContentLength(bytes.length);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, key, byteArrayInputStream, metadata);
client.putObject(putObjectRequest);

您应该考虑使用分段上传 API 以避免将整个 InputStream 加载到内存中。例如:

byte[] bytes = new byte[BUFFER_SIZE];
String uploadId = client.initiateMultipartUpload(new InitiateMultipartUploadRequest(bucket, key)).getUploadId();

int bytesRead = 0;
int partNumber = 1;
List<UploadPartResult> results = new ArrayList<>();
bytesRead = input.read(bytes);
while (bytesRead >= 0) {
    UploadPartRequest part = new UploadPartRequest()
        .withBucketName(bucket)
        .withKey(key)
        .withUploadId(uploadId)
        .withPartNumber(partNumber)
        .withInputStream(new ByteArrayInputStream(bytes, 0, bytesRead))
        .withPartSize(bytesRead);
    results.add(client.uploadPart(part));
    bytesRead = input.read(bytes);
    partNumber++;
}
CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest()
    .withBucketName(bucket)
    .withKey(key)
    .withUploadId(uploadId)
    .withPartETags(results);
client.completeMultipartUpload(completeRequest);

请注意,通过使用 ByteBuffer,您只需手动执行 AWS SDK 已自动为您执行的操作!它仍然将整个流缓冲到内存中,并且与从 SDK 产生警告的原始解决方案一样好。

如果您有另一种方法知道流的长度,例如,当您从文件创建流时,您只能解决内存问题:

void uploadFile(String bucketName, File file) {
    try (final InputStream stream = new FileInputStream(file)) {
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(file.length());
        s3client.putObject(
                new PutObjectRequest(bucketName, file.getName(), stream, metadata)
        );
    }
}

重磅消息! AWS SDK 2.0 内置支持上传文件:

        s3client.putObject(
                (builder) -> builder.bucket(myBucket).key(file.getName()),
                RequestBody.fromFile(file)
        );

还有 RequestBody 获取字符串或缓冲区的方法,这些方法可以自动有效地设置 Content-Length。只有当你有另一种 InputStream 时,你仍然需要自己提供长度——但是现在这种情况应该更罕见了,因为所有其他选项都可用。