例外"The Content-MD5 you specified did not match what we received"



在测试将文件从ec2上传到s3的应用程序时,我遇到了一个前所未有的异常。内容是:

Exception in thread "Thread-1" com.amazonaws.services.s3.model.AmazonS3Exception: The Content-MD5 you specified did not match what we received. (Service: Amazon S3; Status Code: 400; Error Code: BadDigest; Request ID: 972CB8E04388AB20), S3 Extended Request ID: T7bmFnQ2RlGWlJD+aGYfTy97XZw88pbQrwNB8YCezSjyq6O2joxHRP/6ko+Q2zZeGewkw4x/90k=
    at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:1383)
    at com.amazonaws.http.AmazonHttpClient.executeOneRequest(AmazonHttpClient.java:902)
    at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:607)
    at com.amazonaws.http.AmazonHttpClient.doExecute(AmazonHttpClient.java:376)
    at com.amazonaws.http.AmazonHttpClient.executeWithTimer(AmazonHttpClient.java:338)
    at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:287)
    at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:3676)
    at com.amazonaws.services.s3.AmazonS3Client.putObject(AmazonS3Client.java:1439)
    at com.amazonaws.services.s3.transfer.internal.UploadCallable.uploadInOneChunk(UploadCallable.java:131)
    at com.amazonaws.services.s3.transfer.internal.UploadCallable.call(UploadCallable.java:123)
    at com.amazonaws.services.s3.transfer.internal.UploadMonitor.call(UploadMonitor.java:139)
    at com.amazonaws.services.s3.transfer.internal.UploadMonitor.call(UploadMonitor.java:47)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

我能做些什么来修复这个错误?我在应用程序中使用了与以前相同的代码。

我想我已经解决了我的问题。我终于发现我的一些文件在上传过程中发生了变化。因为文件是由另一个线程生成的,所以上传和生成是同时完成的。文件不能立即生成,在生成文件的过程中,它可能同时在上传,文件在上传过程中实际上发生了变化。

文件的md5是AmazonS3Client在上传开始时创建的,然后整个文件被上传到S3,此时文件与开始上传的文件不同,所以md5实际上发生了变化。我把程序修改成了单线程程序,这个问题再也没有出现过。

出现此问题的另一个原因是运行这样的代码(python)

with open(filename, 'r') as fd:
     self._bucket1.put_object(Key=key, Body=fd)
     self._bucket2.put_object(Key=key, Body=fd)

在这种情况下,当文件对象(fd)到达第3行时,它指向文件的末尾,因此我们将得到"ContentMD5"错误,为了避免它,我们需要将文件读取器指向文件中的起始位置

with open(filename, 'r') as fd:
     bucket1.put_object(Key=key, Body=fd)
     fd.seek(0)
     bucket2.put_object(Key=key, Body=fd)

这样我们就不会得到前面提到的Boto错误。

FWIW,我设法找到了一种完全不同的方式来触发这个问题,这需要一个不同的解决方案。

事实证明,如果您决定显式地将ObjectMetadata分配给PutObjectRequest,例如指定cacheControl设置或contentType,那么AWS SDK会对ObjectMetadata实例进行变异,以隐藏它为put请求计算的MD5。这意味着,如果您要放置多个对象,您认为所有对象都应该分配相同的元数据,那么您仍然需要为每个PutObjectRequest创建一个新的ObjectMetadata实例。如果你不这样做,那么它会重用从上一个put请求中计算出的MD5,并且在你试图放入的第二个对象上会出现MD5不匹配的错误。

所以,明确地说,这样做在第二次迭代时会失败:

ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType("text/html");
for(Put obj: thingsToPut)
{
    PutObjectRequest por = 
        new PutObjectRequest(bucketName, obj.s3Key, obj.file);
    por = por.withMetadata(metadata);
    PutObjectResult res = s3.putObject(por);
}

你需要这样做:

for(Put obj: thingsToPut)
{
    ObjectMetadata metadata = new ObjectMetadata(); // <<-- New ObjectMetadata every time!
    metadata.setContentType("text/html");
    PutObjectRequest por =
        new PutObjectRequest(bucketName, obj.s3Key, obj.file);
    por = por.withMetadata(metadata);
    PutObjectResult res = s3.putObject(por);
}

对我来说,在执行upload时,我在params中使用了ContentLength。当它被评论出来时,它运行得很好。

const params = {
  Bucket: "",
  ContentType: "application/json",
  Key: "filename.json",
  // ContentLength: body.length,   <--- what I have commented out
  Body: body
};
await s3.upload(params).promise();

我在做这样的事情时也遇到了这个错误:

InputStream productInputStream = convertImageFileToInputStream(file);
InputStream thumbnailInputStream = generateThumbnail(productInputStream);
String uploadedFileUrl = amazonS3Uploader.uploadToS3(BUCKET_PRODUCTS_IMAGES, productFilename, productInputStream);
String uploadedThumbnailUrl = amazonS3Uploader.uploadToS3(BUCKET_PRODUCTS_IMAGES, productThumbnailFilename, thumbnailInputStream);

generateThumbnail方法使用第三方库操作productInputStream。因为我无法修改第三方库,所以我只是先上传:

InputStream productInputStream = convertImageFileToInputStream(file);
// do this first... 
String uploadedFileUrl = amazonS3Uploader.uploadToS3(BUCKET_PRODUCTS_IMAGES, productFilename, productInputStream);
/// and then this...
InputStream thumbnailInputStream = generateThumbnail(productInputStream);
String uploadedThumbnailUrl = amazonS3Uploader.uploadToS3(BUCKET_PRODUCTS_IMAGES, productThumbnailFilename, thumbnailInputStream);

并在我的generateThumbnail方法中添加了这一行:

productInputStream.reset();

我也遇到了这个问题。我是如何解决这个问题的:

我有一个处理AWS SQS消息的微服务。每条消息将创建多个临时文件,这些文件必须上传到S3。

问题是,临时文件是用固定名称命名的,没有添加任何盐。

因此,在两条消息之间,可以重写要上传的原始文件。

我通过在文件名中添加一个随机salt(这可以是UUID,也可以是以毫秒为单位的当前时间,具体取决于你想要什么)来修复它,之后文件不会被重写,并成功上传到S3。

最新更新