如何在亚马逊的S3上获取文件的md5sum

Swi*_*tch 65 amazon-s3

如果我在亚马逊的S3上有现有文件,那么在不下载文件的情况下获取md5sum的最简单方法是什么?

谢谢

Dun*_*ris 24

对于分段上传,ETag似乎不是MD5(根据Gael Fraiteur的评论).在这些情况下,它包含一个减号和数字的后缀.然而,即使它与MD5的长度相同,即使在减号之前的位似乎也不是MD5.可能后缀是上传的部件数量?

  • 此后缀似乎仅在文件很大(大于5GB)时出现.通过检查我拥有的大量文件,看起来后缀表示上传的部件数量.但是,第一部分似乎没有与原始文件相同的md5哈希.在计算这个哈希时,亚马逊必须为每个部分折叠一些额外的数据.我想知道算法,以便我可以检查我的一些文件. (3认同)
  • 此处描述了哈希算法:http://stackoverflow.com/questions/6591047/etag-definition-changed-in-amazon-s3 (2认同)

小智 22

AWS的文档ETag说:

实体标签是对象的哈希.ETag仅反映对象内容的更改,而不反映其元数据.ETag可能是也可能不是对象数据的MD5摘要.是否取决于对象的创建方式以及如何加密,如下所述:

  • 由PUT对象,POST对象或复制操作或通过AWS管理控制台创建并由SSE-S3或纯文本加密的对象具有ETag,这些ETag是其对象数据的MD5摘要.
  • 由PUT对象,POST对象或复制操作或通过AWS管理控制台创建并由SSE-C或SSE-KMS加密的对象具有不是其对象数据的MD5摘要的ETag.
  • 如果通过"分段上传"或"零件复制"操作创建对象,则无论加密方法如何,ETag都不是MD5摘要.

参考:http://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html


小智 9

下面是我将本地文件校验和与 s3 etag 进行比较的工作。我用过 Python

def md5_checksum(filename):
    m = hashlib.md5()
    with open(filename, 'rb') as f:
        for data in iter(lambda: f.read(1024 * 1024), b''):
            m.update(data)
   
    return m.hexdigest()


def etag_checksum(filename, chunk_size=8 * 1024 * 1024):
    md5s = []
    with open(filename, 'rb') as f:
        for data in iter(lambda: f.read(chunk_size), b''):
            md5s.append(hashlib.md5(data).digest())
    m = hashlib.md5(b"".join(md5s))
    print('{}-{}'.format(m.hexdigest(), len(md5s)))
    return '{}-{}'.format(m.hexdigest(), len(md5s))

def etag_compare(filename, etag):
    et = etag[1:-1] # strip quotes
    print('et',et)
    if '-' in et and et == etag_checksum(filename):
        return True
    if '-' not in et and et == md5_checksum(filename):
        return True
    return False


def main():   
    session = boto3.Session(
        aws_access_key_id=s3_accesskey,
        aws_secret_access_key=s3_secret
    )
    s3 = session.client('s3')
    obj_dict = s3.get_object(Bucket=bucket_name, Key=your_key)

    etag = (obj_dict['ETag'])
    print('etag', etag)
    
    validation = etag_compare(filename,etag)
    print(validation)
    etag_checksum(filename, chunk_size=8 * 1024 * 1024)
    return validation
Run Code Online (Sandbox Code Playgroud)


Nel*_*ira 8

这是一个非常古老的问题,但我很难找到下面的信息,这是我能找到的第一个地方之一,所以我想详细说明一下,以防有人需要。

ETag 是一个 MD5。但是对于分段上传的文件,MD5 是根据每个上传部分的 MD5 的串联计算得出的。所以你不需要在服务器中计算 MD5。只需获取 ETag 即可。

正如@EmersonFarrugia 在这个答案中所说:

假设您上传了一个 14MB 的文件,而您的部分大小为 5MB。计算每个部分对应的3个MD5校验和,即前5MB、后5MB、后4MB的校验和。然后取它们连接的校验和。由于 MD5 校验和是二进制数据的十六进制表示,只需确保采用解码二进制连接的 MD5,而不是 ASCII 或 UTF-8 编码连接的 MD5。完成后,添加连字符和零件数以获取 ETag。

所以你唯一需要的其他东西就是 ETag 和上传部分的大小。但是 ETag 有一个 -NumberOfParts 后缀。因此,您可以将尺寸除以后缀并获得零件尺寸。5Mb 是最小部分大小和默认值。部分大小必须是整数,所以你不能得到每个部分大小为 7,25Mb 的东西。所以应该很容易获得零件尺寸信息。

这是一个在 osx 中制作的脚本,评论中有 Linux 版本:https : //gist.github.com/emersonf/7413337

如果以后无法再访问上面的页面,我将把这两个脚本都留在这里:

Linux版本:

#!/bin/bash
set -euo pipefail
if [ $# -ne 2 ]; then
    echo "Usage: $0 file partSizeInMb";
    exit 0;
fi
file=$1
if [ ! -f "$file" ]; then
    echo "Error: $file not found." 
    exit 1;
fi
partSizeInMb=$2
fileSizeInMb=$(du -m "$file" | cut -f 1)
parts=$((fileSizeInMb / partSizeInMb))
if [[ $((fileSizeInMb % partSizeInMb)) -gt 0 ]]; then
    parts=$((parts + 1));
fi
checksumFile=$(mktemp -t s3md5.XXXXXXXXXXXXX)
for (( part=0; part<$parts; part++ ))
do
    skip=$((partSizeInMb * part))
    $(dd bs=1M count=$partSizeInMb skip=$skip if="$file" 2> /dev/null | md5sum >> $checksumFile)
done
etag=$(echo $(xxd -r -p $checksumFile | md5sum)-$parts | sed 's/ --/-/')
echo -e "${1}\t${etag}"
rm $checksumFile
Run Code Online (Sandbox Code Playgroud)

OSX 版本:

#!/bin/bash

if [ $# -ne 2 ]; then
    echo "Usage: $0 file partSizeInMb";
    exit 0;
fi

file=$1

if [ ! -f "$file" ]; then
    echo "Error: $file not found." 
    exit 1;
fi

partSizeInMb=$2
fileSizeInMb=$(du -m "$file" | cut -f 1)
parts=$((fileSizeInMb / partSizeInMb))
if [[ $((fileSizeInMb % partSizeInMb)) -gt 0 ]]; then
    parts=$((parts + 1));
fi

checksumFile=$(mktemp -t s3md5)

for (( part=0; part<$parts; part++ ))
do
    skip=$((partSizeInMb * part))
    $(dd bs=1m count=$partSizeInMb skip=$skip if="$file" 2>/dev/null | md5 >>$checksumFile)
done

echo $(xxd -r -p $checksumFile | md5)-$parts
rm $checksumFile
Run Code Online (Sandbox Code Playgroud)


nea*_*mcb 7

截至 2022 年 2 月 25 日,S3 具有新的校验和检索功能GetObjectAttributes

\n

Amazon S3 的新 \xe2\x80\x93 附加校验和算法 | AWS 新闻博客

\n
\n

校验和检索 \xe2\x80\x93 新GetObjectAttributes函数返回对象和每个部分(如果适用)的校验和。

\n
\n

该函数支持 SHA-1、SHA-256、CRC-32 和 CRC-32C,用于检查传输的完整性。

\n

更新:虽然这种GetObjectAttributes方法在许多情况下都有效,但在某些情况下,例如控制台上传,校验和是根据 16 MB 块计算的。请参见检查对象完整性

\n
\n

当您使用 AWS 管理控制台执行某些操作时,如果对象大小超过 16 MB,Amazon S3 将使用分段上传。在这种情况下,校验和不是整个对象的直接校验和,而是基于每个单独部分的校验和值的计算。

\n

例如,考虑您使用 REST API 作为单部分直接上传上传的大小为 100 MB 的对象。本例中的校验和是整个对象的校验和。如果您稍后使用控制台重命名该对象、复制它、更改存储类或编辑元数据,Amazon S3 将使用分段上传功能来更新该对象。因此,Amazon S3 为对象创建一个新的校验和值,该值是根据各个部分的校验和值计算得出的。

\n
\n

看起来 MD5 实际上并不是新功能的一个选项,所以这可能无法解决您原来的问题,但 MD5 由于多种原因而被弃用,如果使用备用校验和对您有用,这可能就是您的选择正在寻找。

\n


Pit*_*ong 6

对于那些花时间四处搜索以找出为什么 md5 与 S3 中的 ETag 不同的人。

ETag会根据数据的chuck进行计算,并连接所有md5hash,再次进行md5散列,并将chunk的数量保留在末尾。

这是生成哈希的 C# 版本

    string etag = HashOf("file.txt",8);
Run Code Online (Sandbox Code Playgroud)

源代码

    private string HashOf(string filename,int chunkSizeInMb)
    {
        string returnMD5 = string.Empty;
        int chunkSize = chunkSizeInMb * 1024 * 1024;

        using (var crypto = new MD5CryptoServiceProvider())
        {
            int hashLength = crypto.HashSize/8;

            using (var stream = File.OpenRead(filename))
            {
                if (stream.Length > chunkSize)
                {
                    int chunkCount = (int)Math.Ceiling((double)stream.Length/(double)chunkSize);

                    byte[] hash = new byte[chunkCount*hashLength];
                    Stream hashStream = new MemoryStream(hash);

                    long nByteLeftToRead = stream.Length;
                    while (nByteLeftToRead > 0)
                    {
                        int nByteCurrentRead = (int)Math.Min(nByteLeftToRead, chunkSize);
                        byte[] buffer = new byte[nByteCurrentRead];
                        nByteLeftToRead -= stream.Read(buffer, 0, nByteCurrentRead);

                        byte[] tmpHash = crypto.ComputeHash(buffer);

                        hashStream.Write(tmpHash, 0, hashLength);

                    }

                    returnMD5 = BitConverter.ToString(crypto.ComputeHash(hash)).Replace("-", string.Empty).ToLower()+"-"+ chunkCount;
                }
                else {
                    returnMD5 = BitConverter.ToString(crypto.ComputeHash(stream)).Replace("-", string.Empty).ToLower();

                }
                stream.Close();
            }
        }
        return returnMD5;
    }
Run Code Online (Sandbox Code Playgroud)