如何使用Content-MD5将对象放置到s3

时间:2018-10-18 18:24:14

标签: python amazon-web-services amazon-s3 boto3

我尝试使用boto3将XML文件上传到S3。按照Amazon的建议,我想发送数据的Base64编码MD5-128位摘要(Content-MD5)。

https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Object.put

我的代码:

with open(file, 'rb') as tempfile:
   body = tempfile.read()
tempfile.close()

hash_object = hashlib.md5(body)
base64_md5 = base64.encodebytes(hash_object.digest())

response = s3.Object(self.bucket, self.key + file).put(
            Body=body.decode(self.encoding),
            ACL='private',
            Metadata=metadata,
            ContentType=self.content_type,
            ContentEncoding=self.encoding,
            ContentMD5=str(base64_md5)
        )

当我尝试这个str(base64_md5)创建一个像'b'ZpL06Osuws3qFQJ8ktdBOw == \ n''的字符串

在这种情况下,我收到此错误消息:

An error occurred (InvalidDigest) when calling the PutObject operation: The Content-MD5 you specified was invalid.

出于测试目的,我只复制了前面没有'b'的值:'ZpL06Osuws3qFQJ8ktdBOw == \ n'

然后我收到此错误消息:

botocore.exceptions.HTTPClientError: An HTTP Client raised and unhandled exception: Invalid header value b'hvUe19qHj7rMbwOWVPEv6Q==\n'

有人可以帮我保存如何将“上载文件”保存到S3吗?

谢谢

橄榄

2 个答案:

答案 0 :(得分:6)

从@Isaac Fife的示例开始,将其精简以识别需要什么,而不是什么,并包括导入内容,并使其完整reproducible example

(您唯一需要做的更改就是使用自己的存储桶名称)

import base64
import hashlib
import boto3

contents = "hello world!"
md = hashlib.md5(contents.encode('utf-8')).digest()
contents_md5 = base64.b64encode(md).decode('utf-8')

boto3.client('s3').put_object(
  Bucket="mybucket",
  Key="test",
  Body=contents,
  ContentMD5=contents_md5
)

经验教训:首先,您尝试生成的MD5看起来不会像“上载”返回的那样。我们实际上需要一个base64版本,它返回md.hexdigest()版本。十六进制是base16,不是base64。

答案 1 :(得分:0)

(Python 3.7)

花了我几个小时来解决这个问题,因为您得到的唯一错误是“您指定的Content-MD5无效。”超级有用,对调试非常有用...无论如何,这是我在重构前实际上用来使文件正确上传的代码。

json_results = json_converter.convert_to_json(result)
json_results_utf8 = json_results.encode('utf-8')
content_md5 = md5.get_content_md5(json_results_utf8)
content_md5_string = content_md5.decode('utf-8')
metadata = {
    "md5chksum": content_md5_string
}
s3 = boto3.resource('s3', config=Config(signature_version='s3v4'))
obj = s3.Object(bucket, 'filename.json')
obj.put(
    Body=json_results_utf8,
    ContentMD5=content_md5_string,
    ServerSideEncryption='aws:kms',
    Metadata=metadata,
    SSEKMSKeyId=key_id)

和散列

def get_content_md5(data):
    digest = hashlib.md5(data).digest()
    return base64.b64encode(digest)

对我来说,最困难的部分是弄清楚在过程的每个步骤中需要使用哪种编码,而不是非常熟悉当时在python中如何存储字符串。

get_content_md5仅采用类似utf-8字节的对象,并返回该对象。但是要将md5哈希传递给aws,它必须是字符串。您必须先对其进行解码,然后再将其提供给ContentMD5

专业提示-Body另一方面,需要指定字节或可搜索的对象。在将文件seek(0)传递给AWS之前,请确保是否将{{1}}传递给文件的开头,否则它将不匹配。因此,使用字节不太容易出错,imo。