【问题标题】:Howto put object to s3 with Content-MD5如何使用 Content-MD5 将对象放入 s3
【发布时间】:2023-03-05 21:00:01
【问题描述】:

我尝试使用 boto3 将 XML 文件上传到 S3。根据亚马逊的建议,我想发送一个 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 吗?

谢谢,

奥利弗

【问题讨论】:

  • 我可能错了,但我认为ContentMD5=str(base64_md5) 应该是ContentMD5=hash_object.hexdigest())。我认为它不应该是 base64 编码的。
  • 我尝试只发送 hexdigest 但错误消息相同:botocore.exceptions.ClientError: An error occurred (InvalidDigest) when calling the PutObject operation: The Content-MD5 you specified was invalid. 这是 boto3 描述: ContentMD5 (string) -- 零件数据的 base64 编码的 128 位 MD5 摘要.

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


【解决方案1】:

从@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() 版本。 hex是base16,不是base64。

【讨论】:

  • 这在使用 boto3 分段上传时有效吗?因为我注意到使用多部分上传时,etag 的结构完全不同。
  • 这在 python lambda 中对我有用。我很欣赏不需要花时间弄清楚发生了什么(感谢@Isaac)并有一个干净的可重现示例(感谢@tedder42!)
  • 如果您的内容已经是二进制的(因为 OP 正在打开文件 'rb'),只需删除第 6 行 md 赋值语句的 .encode('utf-8') 部分.
【解决方案2】:

(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 需要指定字节或可查找对象。确保在将其传递给 AWS 之前,是否将 seek(0) 的可搜索对象传递到文件的开头,否则 MD5 将不匹配。因此,使用字节不太容易出错,imo。

【讨论】:

  • 我精简了这个并删除了不太相关的部分。
  • ContentMD5=content_md5_string中已经使用相同的md5时,为什么还要使用metadata = { "md5chksum": content_md5_string }
猜你喜欢
  • 1970-01-01
  • 2022-09-26
  • 1970-01-01
  • 1970-01-01
  • 2016-06-17
  • 1970-01-01
  • 1970-01-01
  • 2020-09-21
相关资源
最近更新 更多