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


77

如果我在Amazon S3上已有文件,那么无需下载文件即可获取其md5sum的最简单方法是什么?

谢谢


1
ETag标头是MD5,但不适用于多部分文件。这里是你如何使用它的详细信息:stackoverflow.com/questions/6591047/...
R03

2
如果不检索整个对象并进行本地计算,就无法在S3对象上计算MD5?当前,没有任何答案实际上解决了这个非常简单的问题,而是纯粹专注于ETag。大多数提出使用ETag的答案甚至都承认,它不是计算得出的MD5的合适替代品。
bsplosion

Answers:


31

AWS的文档ETag说:

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

  • 由PUT对象,POST对象或复制操作创建的对象,或者通过AWS管理控制台创建的,并由SSE-S3或纯文本加密的对象,其ETag是其对象数据的MD5摘要。
  • 由PUT对象,POST对象或复制操作创建的对象,或者通过AWS管理控制台创建并由SSE-C或SSE-KMS加密的对象,其ETag不是其对象数据的MD5摘要。
  • 如果通过“分段上传”或“部分复制”操作创建了对象,则无论采用哪种加密方法,ETag都不是MD5摘要。

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


26

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


3
该后缀似乎仅在文件较大(大于5GB)时出现。通过检查我拥有的几个大文件,后缀确实代表了上传的零件数。但是,第一部分似乎没有与原始文件相同的md5哈希。在计算此哈希时,亚马逊必须为每个部分折叠一些额外的数据。我想知道算法,以便可以检查一些文件。
broc.seib 2012年

2
哈希算法在这里描述:stackoverflow.com/questions/6591047/...
Nakedible

@ broc.seib我看到了一个后缀,该文件的后缀要小得多,例如18.3MB。我想知道这是否取决于上传文件的方式。我正在使用aws s3 cp ...
马克

@Mark此处发布的答案有更多详细信息:stackoverflow.com/a/19896823/516910
broc.seib

7

在下面的工作对我来说是将本地文件校验和与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


your_key一样的filename
刘杰森

有不同的。
李欣

使用该etag_checksum功能时,我遇到了小于的文件问题chunkt_size。一个简单的文件大小测试(os.path.getsize(filename) < chunk_size)解决了这个问题,以防其他人也有此问题。在这种情况下,哈希为hashlib.md5(f.read())
KingBugAndTheCodeWizard

4

对于任何花时间四处寻找以找出为什么md5与S3中的ETag不同的人。

ETag会根据数据量计算并连接所有md5hash以再次使md5散列,并在最后保留块数。

这是生成哈希值的C#版本

    string etag = HashOf("file.txt",8);

源代码

    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;
    }

对于小文件,此代码对我有用。大文件给了我一个不同的哈希值
Tono Nam

文件大小是多少?
Pitipong Guntawong

如何获取s3多部分对象键的块大小?
丹尼尔(Daniel)

取决于上传软件。您可以在通过AWS CLI上传时设置块大小。(默认大小:8MB)ref:docs.aws.amazon.com/cli/latest/topic/s3-config.html
Pitipong Guntawong

4

这是一个非常老的问题,但是我很难找到下面的信息,这是我可以找到的第一个地方,因此我想详细介绍一下以防任何人需要。

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

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

1

我发现s3cmd具有--list-md5选项,可与ls命令一起使用,例如

s3cmd ls --list-md5 s3://bucket_of_mine/

希望这可以帮助。


这很方便,但是正如在其他一些答案中提到的那样,在某些文件上,这不是实际的MD5总和,而是其他类型的哈希。
伊恩·格林利夫·杨

2
我检查了s3cmd源代码,并在上传时将md5存储在元数据中。因此,对于使用s3cmd上传的对象或以单个块上传的对象,此命令仅会打印md5
ZAB


0

最简单的方法是,在将以下文件上传到存储桶之前,自己将校验和设置为元数据:

ObjectMetadata md = new ObjectMetadata();
md.setContentMD5("foobar");
PutObjectRequest req = new PutObjectRequest(BUCKET, KEY, new File("/path/to/file")).withMetadata(md);
tm.upload(req).waitForUploadResult();

现在,您无需下载文件即可访问这些元数据:

ObjectMetadata md2 = s3Client.getObjectMetadata(BUCKET, KEY);
System.out.println(md.getContentMD5());

来源:https : //github.com/aws/aws-sdk-java/issues/1711


-2

这对我有用。在PHP中,您可以使用以下方法比较本地文件和亚马逊文件之间的校验和:



    // get localfile md5
    $checksum_local_file = md5_file ( '/home/file' );

    // compare checksum between localfile and s3file    
    public function compareChecksumFile($file_s3, $checksum_local_file) {

        $Connection = new AmazonS3 ();
        $bucket = amazon_bucket;
        $header = $Connection->get_object_headers( $bucket, $file_s3 );

        // get header
        if (empty ( $header ) || ! is_object ( $header )) {
            throw new RuntimeException('checksum error');
        }
        $head = $header->header;
        if (empty ( $head ) || !is_array($head)) {
            throw new RuntimeException('checksum error');
        }
        // get etag (md5 amazon)
        $etag = $head['etag'];
        if (empty ( $etag )) {
            throw new RuntimeException('checksum error');
        }
        // remove quotes
        $checksumS3 = str_replace('"', '', $etag);

        // compare md5
        if ($checksum_local_file === $checksumS3) {
            return TRUE;
        } else {
            return FALSE;
        }
    }


-2

这是从c#转换为PowerShell中对象的S3 ETag的代码。

function Get-ETag {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory=$true)]
    [string]$Path,
    [Parameter(Mandatory=$true)]
    [int]$ChunkSizeInMb
  )

  $returnMD5 = [string]::Empty
  [int]$chunkSize = $ChunkSizeInMb * [Math]::Pow(2, 20)

  $crypto = New-Object System.Security.Cryptography.MD5CryptoServiceProvider
  [int]$hashLength = $crypto.HashSize / 8

  $stream = [System.IO.File]::OpenRead($Path)

  if($stream.Length -gt $chunkSize) {
    $chunkCount = [int][Math]::Ceiling([double]$stream.Length / [double]$chunkSize)
    [byte[]]$hash = New-Object byte[]($chunkCount * $hashLength)
    $hashStream = New-Object System.IO.MemoryStream(,$hash)
    [long]$numBytesLeftToRead = $stream.Length
    while($numBytesLeftToRead -gt 0) {
      $numBytesCurrentRead = [int][Math]::Min($numBytesLeftToRead, $chunkSize)
      $buffer = New-Object byte[] $numBytesCurrentRead
      $numBytesLeftToRead -= $stream.Read($buffer, 0, $numBytesCurrentRead)
      $tmpHash = $crypto.ComputeHash($buffer)
      $hashStream.Write($tmpHash, 0, $hashLength)
    }
    $returnMD5 = [System.BitConverter]::ToString($crypto.ComputeHash($hash)).Replace("-", "").ToLower() + "-" + $chunkCount
  }
  else {
    $returnMD5 = [System.BitConverter]::ToString($crypto.ComputeHash($stream)).Replace("-", "").ToLower()
  }

  $stream.Close()  
  $returnMD5
}

-3

这是根据2017获取MD5哈希的代码

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Base64;
public class GenerateMD5 {
public static void main(String args[]) throws Exception{
    String s = "<CORSConfiguration> <CORSRule> <AllowedOrigin>http://www.example.com</AllowedOrigin> <AllowedMethod>PUT</AllowedMethod> <AllowedMethod>POST</AllowedMethod> <AllowedMethod>DELETE</AllowedMethod> <AllowedHeader>*</AllowedHeader> <MaxAgeSeconds>3000</MaxAgeSeconds> </CORSRule> <CORSRule> <AllowedOrigin>*</AllowedOrigin> <AllowedMethod>GET</AllowedMethod> <AllowedHeader>*</AllowedHeader> <MaxAgeSeconds>3000</MaxAgeSeconds> </CORSRule> </CORSConfiguration>";

        MessageDigest md = MessageDigest.getInstance("MD5");
        md.update(s.getBytes());
        byte[] digest = md.digest();
        StringBuffer sb = new StringBuffer();
        /*for (byte b : digest) {
            sb.append(String.format("%02x", b & 0xff));
        }*/
        System.out.println(sb.toString());
        StringBuffer sbi = new StringBuffer();
        byte [] bytes = Base64.encodeBase64(digest);
        String finalString = new String(bytes);
        System.out.println(finalString);
    }
}

注释的代码是大多数人将其更改为十六进制时出错的地方

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.