AmazonS3 putObject with InputStream length 示例

我正在使用Java将文件上传到S3 - 这是我到目前为止得到的:

AmazonS3 s3 = new AmazonS3Client(new BasicAWSCredentials("XX","YY"));

List<Bucket> buckets = s3.listBuckets();

s3.putObject(new PutObjectRequest(buckets.get(0).getName(), fileName, stream, new ObjectMetadata()));

文件正在上传,但当我未设置内容长度时,会引发警告:

com.amazonaws.services.s3.AmazonS3Client putObject: No content length specified for stream > data.  Stream contents will be buffered in memory and could result in out of memory errors.

这是我正在上传的文件,变量是一个 ,我可以从中获取字节数组,如下所示:。streamInputStreamIOUtils.toByteArray(stream)

因此,当我尝试设置内容长度和MD5(从这里获取)时,如下所示:

// get MD5 base64 hash
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(IOUtils.toByteArray(stream));
byte[] resultByte = messageDigest.digest();
String hashtext = new String(Hex.encodeHex(resultByte));

ObjectMetadata meta = new ObjectMetadata();
meta.setContentLength(IOUtils.toByteArray(stream).length);
meta.setContentMD5(hashtext);

它会导致以下错误从 S3 返回:

您指定的内容 MD5 无效。

我做错了什么?

任何帮助赞赏!

附言我使用的是 Google App Engine - 我无法将文件写入磁盘或创建临时文件,因为 AppEngine 不支持 FileOutputStream。


答案 1

因为最初的问题从未得到回答,而且我不得不遇到同样的问题,所以MD5问题的解决方案是S3不需要我们通常考虑的十六进制编码的MD5字符串。

相反,我必须这样做。

// content is a passed in InputStream
byte[] resultByte = DigestUtils.md5(content);
String streamMD5 = new String(Base64.encodeBase64(resultByte));
metaData.setContentMD5(streamMD5);

从本质上讲,他们想要的MD5值是Base64编码的原始MD5字节数组,而不是十六进制字符串。当我切换到这个时,它开始对我很好。


答案 2

如果您要做的只是解决来自 amazon 的内容长度错误,那么您可以只将输入流中的字节读取到 Long,然后将其添加到元数据中。

/*
 * Obtain the Content length of the Input stream for S3 header
 */
try {
    InputStream is = event.getFile().getInputstream();
    contentBytes = IOUtils.toByteArray(is);
} catch (IOException e) {
    System.err.printf("Failed while reading bytes from %s", e.getMessage());
} 

Long contentLength = Long.valueOf(contentBytes.length);

ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(contentLength);

/*
 * Reobtain the tmp uploaded file as input stream
 */
InputStream inputStream = event.getFile().getInputstream();

/*
 * Put the object in S3
 */
try {

    s3client.putObject(new PutObjectRequest(bucketName, keyName, inputStream, metadata));

} catch (AmazonServiceException ase) {
    System.out.println("Error Message:    " + ase.getMessage());
    System.out.println("HTTP Status Code: " + ase.getStatusCode());
    System.out.println("AWS Error Code:   " + ase.getErrorCode());
    System.out.println("Error Type:       " + ase.getErrorType());
    System.out.println("Request ID:       " + ase.getRequestId());
} catch (AmazonClientException ace) {
    System.out.println("Error Message: " + ace.getMessage());
} finally {
    if (inputStream != null) {
        inputStream.close();
    }
}

您需要使用此确切的方法将输入流读取两次,因此,如果要上传非常大的文件,则可能需要将其读取一次到数组中,然后从那里读取它。


推荐