boto 获取 S3 文件的 MD5
我有一个情况,就是我需要把几百个文件上传到我的S3存储桶,使用的是分段上传。在每次上传后,我需要确保上传的文件没有损坏(也就是检查数据的完整性)。目前,我的做法是上传文件后,再把它下载回来,计算一下内容的md5
值,然后和本地文件的md5
值进行比较。大致就是这样:
conn = S3Connection('access key', 'secretkey')
bucket = conn.get_bucket('bucket_name')
source_path = 'file_to_upload'
source_size = os.stat(source_path).st_size
mp = bucket.initiate_multipart_upload(os.path.basename(source_path))
chunk_size = 52428800
chunk_count = int(math.ceil(source_size / chunk_size))
for i in range(chunk_count + 1):
offset = chunk_size * i
bytes = min(chunk_size, source_size - offset)
with FileChunkIO(source_path, 'r', offset=offset, bytes=bytes) as fp:
mp.upload_part_from_file(fp, part_num=i + 1, md5=k.compute_md5(fp, bytes))
mp.complete_upload()
obj_key = bucket.get_key('file_name')
print(obj_key.md5) #prints None
print(obj_key.base64md5) #prints None
content = bucket.get_key('file_name').get_contents_as_string()
# compute the md5 on content
这种方法有点浪费,因为它会消耗两倍的带宽。我试过
bucket.get_key('file_name').md5
bucket.get_key('file_name').base64md5
但两个都返回了None。
有没有其他方法可以在不下载整个文件的情况下计算md5
呢?
4 个回答
1
自2016年以来,最简单的方法是在进行PutObject请求时,提供一个叫做--content-md5
的参数。这样,AWS会检查你提供的MD5值是否和他们计算出来的MD5值一致。这种方法也适用于分片上传和大于5GB的文件。
下面是来自知识中心的一个示例调用:
aws s3api put-object --bucket awsexamplebucket --key awsexampleobject.txt --body awsexampleobjectpath --content-md5 examplemd5value1234567== --metadata md5checksum=examplemd5value1234567==
https://aws.amazon.com/premiumsupport/knowledge-center/data-integrity-s3/
3
你可以通过 e_tag
属性来恢复 md5
,而不需要下载文件,方法如下:
boto3.resource('s3').Object(<BUCKET_NAME>, file_path).e_tag[1 :-1]
然后使用这个函数来比较经典的 S3 文件:
def md5_checksum(file_path):
m = hashlib.md5()
with open(file_path, 'rb') as f:
for data in iter(lambda: f.read(1024 * 1024), b''):
m.update(data)
return m.hexdigest()
或者使用这个函数来处理多部分文件:
def etag_checksum(file_path, chunk_size=8 * 1024 * 1024):
md5s = []
with open(file_path, 'rb') as f:
for data in iter(lambda: f.read(chunk_size), b''):
md5s.append(hashlib.md5(data).digest())
m = hashlib.md5("".join(md5s))
return '{}-{}'.format(m.hexdigest(), len(md5s))
最后,使用这个函数在两者之间进行选择:
def md5_compare(file_path, s3_file_md5):
if '-' in s3_file_md5 and s3_file_md5 == etag_checksum(file_path):
return True
if '-' not in s3_file_md5 and s3_file_md5 == md5_checksum(file_path):
return True
print("MD5 not equals for file " + file_path)
return False
感谢来源: https://zihao.me/post/calculating-etag-for-aws-s3-objects/
11
我使用boto3这个库里的head_object
方法来获取ETag。
import boto3
import botocore
def s3_md5sum(bucket_name, resource_name):
try:
md5sum = boto3.client('s3').head_object(
Bucket=bucket_name,
Key=resource_name
)['ETag'][1:-1]
except botocore.exceptions.ClientError:
md5sum = None
pass
return md5sum
26
没错,
你可以用 bucket.get_key('file_name').etag[1 :-1]
这个方法。
这样就能获取文件的MD5值,而不用下载文件的内容。