Cách lấy md5sum của tệp trên S3 của Amazon


80

Nếu tôi có các tệp hiện có trên S3 của Amazon, thì cách dễ nhất để lấy md5sum của chúng mà không cần phải tải tệp xuống là gì?

Cảm ơn


1
Tiêu đề ETag là MD5, nhưng không dành cho tệp nhiều phần. Dưới đây là thông tin thêm về cách bạn có thể sử dụng nó: stackoverflow.com/questions/6591047/...
R03

2
Không có cách nào để tính toán MD5 trên một đối tượng S3 mà không truy xuất toàn bộ đối tượng và tính toán cục bộ? Hiện tại, không có câu trả lời nào thực sự giải quyết câu hỏi rất đơn giản này và thay vào đó tập trung hoàn toàn vào ETag. Hầu hết các câu trả lời đề xuất việc sử dụng ETag thậm chí thừa nhận nó không phải là sự thay thế phù hợp cho MD5 được tính toán.
bsplosion

Câu trả lời:


33

Tài liệu của AWS ETagnói:

Thẻ thực thể là một hàm băm của đối tượng. ETag chỉ phản ánh những thay đổi đối với nội dung của một đối tượng, không phản ánh siêu dữ liệu của nó. ETag có thể có hoặc không phải là bản tóm tắt MD5 của dữ liệu đối tượng. Việc có hay không tùy thuộc vào cách đối tượng được tạo ra và cách nó được mã hóa như mô tả bên dưới:

  • Các đối tượng được tạo bởi Đối tượng PUT, Đối tượng POST hoặc thao tác Sao chép hoặc thông qua Bảng điều khiển quản lý AWS và được mã hóa bằng SSE-S3 hoặc văn bản rõ, có thẻ ETags là bản tóm tắt MD5 của dữ liệu đối tượng của chúng.
  • Các đối tượng được tạo bởi Đối tượng PUT, Đối tượng POST hoặc thao tác Sao chép hoặc thông qua Bảng điều khiển quản lý AWS và được mã hóa bởi SSE-C hoặc SSE-KMS, có thẻ ETags không phải là bản tóm tắt MD5 của dữ liệu đối tượng của chúng.
  • Nếu một đối tượng được tạo bởi thao tác Tải lên nhiều phần hoặc Sao chép một phần, ETag không phải là thông báo MD5, bất kể phương pháp mã hóa là gì.

Tham khảo: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html


26

ETag dường như không phải là MD5 để tải lên nhiều phần (theo nhận xét của Gael Fraiteur). Trong những trường hợp này, nó chứa một hậu tố là số trừ và một số. Tuy nhiên, ngay cả bit trước dấu trừ dường như không phải là MD5, mặc dù nó có cùng độ dài với MD5. Có thể hậu tố là số phần được tải lên?


3
Hậu tố này dường như chỉ xuất hiện khi tệp lớn (lớn hơn 5GB). Bằng cách kiểm tra một số tệp lớn mà tôi có, có vẻ như hậu tố thể hiện số phần được tải lên. Tuy nhiên, phần đầu tiên dường như không có cùng băm md5 như tệp gốc. Khi tính toán băm này, amazon phải gấp trong một số dữ liệu bổ sung cho mỗi phần. Tôi muốn biết thuật toán để có thể kiểm tra một số tệp của mình.
broc.seib

2
Thuật toán băm được mô tả tại đây: stackoverflow.com/questions/6591047/…
Nakedible

@ broc.seib Tôi thấy một hậu tố cho các tệp nhỏ hơn nhiều, chẳng hạn như một tệp 18,3MB. Tôi tự hỏi nếu nó phụ thuộc vào những gì được sử dụng để tải lên tệp; Tôi đang sử dụngaws s3 cp ...
Đánh dấu

@ Mark Câu trả lời được đăng ở đây có chi tiết hơn: stackoverflow.com/a/19896823/516910
broc.seib

7

Dưới đây là công việc để tôi so sánh tổng kiểm tra tệp cục bộ với s3 etag. Tôi đã sử dụng 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_keygiống như filename?
Jason Liu

Có khác nhau.
li xin

Khi sử dụng etag_checksumhàm này, tôi đã gặp sự cố với các tệp nhỏ hơn chunkt_size. Một bài kiểm tra đơn giản về kích thước tệp ( os.path.getsize(filename) < chunk_size) đã sửa lỗi này, trong trường hợp bất kỳ ai khác cũng gặp sự cố này. Trong trường hợp đó, hàm băm làhashlib.md5(f.read())
KingBugAndTheCodeWizard

5

Đây là một câu hỏi rất cũ, nhưng tôi đã rất khó tìm thông tin bên dưới, và đây là một trong những nơi đầu tiên tôi có thể tìm thấy, vì vậy tôi muốn trình bày chi tiết để phòng trường hợp ai cần.

ETag là một MD5. Nhưng đối với các tệp được tải lên Multipart, MD5 được tính từ việc ghép các MD5 của mỗi phần được tải lên. Vì vậy, bạn không cần phải tính MD5 trong máy chủ. Chỉ cần lấy ETag và tất cả.

Như @EmersonFarrugia đã nói trong câu trả lời này :

Giả sử bạn đã tải lên một tệp 14MB và kích thước một phần của bạn là 5MB. Tính 3 tổng kiểm tra MD5 tương ứng với mỗi phần, tức là tổng kiểm tra của 5MB đầu tiên, 5MB thứ hai và 4MB cuối cùng. Sau đó, lấy tổng kiểm tra của nối của chúng. Vì tổng kiểm tra MD5 là biểu diễn hex của dữ liệu nhị phân, chỉ cần đảm bảo bạn lấy MD5 của phép ghép nhị phân được giải mã, không phải của phép nối được mã hóa ASCII hoặc UTF-8. Khi hoàn tất, hãy thêm dấu gạch ngang và số phần để lấy ETag.

Vì vậy, thứ duy nhất bạn cần là ETag và kích thước phần tải lên. Nhưng ETag có hậu tố -NumberOfParts. Vì vậy, bạn có thể chia kích thước cho hậu tố và lấy kích thước một phần. 5Mb là kích thước bộ phận tối thiểu và giá trị mặc định. Kích thước một phần phải là số nguyên, vì vậy bạn không thể nhận được những thứ như kích thước mỗi phần là 7,25Mb. Vì vậy, sẽ dễ dàng có được thông tin về kích thước bộ phận

Đây là một tập lệnh để thực hiện điều này trong osx, với một phiên bản Linux trong nhận xét: https://gist.github.com/emersonf/7413337

Tôi sẽ để lại cả hai tập lệnh ở đây trong trường hợp trang trên không còn truy cập được trong tương lai:

Phiên bản 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

Phiên bản 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

4

Đối với bất kỳ ai dành thời gian tìm kiếm xung quanh để tìm ra lý do tại sao md5 không giống với ETag trong S3.

ETag sẽ tính toán dựa trên phân đoạn dữ liệu và ghép tất cả md5hash để tạo lại băm md5 và giữ nguyên số lượng đoạn cuối.

Đây là phiên bản C # để tạo băm

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

mã nguồn

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

Mã này phù hợp với tôi cho các tệp nhỏ. File lớn mang lại cho tôi một hash khác nhau
Tono Nam

kích thước của tệp là bao nhiêu?
Pitipong Guntawong

làm thế nào để lấy kích thước chunk của khóa đối tượng nhiều phần s3?
Daniel

Phụ thuộc vào phần mềm tải lên. Bạn có thể thiết lập kích thước đoạn khi bạn tải lên thông qua AWS CLI (Mặc định là: 8MB) ref:. Docs.aws.amazon.com/cli/latest/topic/s3-config.html
Pitipong Guntawong

1

Tôi thấy rằng s3cmd có tùy chọn --list-md5 có thể được sử dụng với lệnh ls, ví dụ:

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

Hi vọng điêu nay co ich.


Điều này rất hữu ích, nhưng như đã đề cập trong một số câu trả lời khác, trên một số tệp, đây sẽ không phải là tổng MD5 thực tế, mà là một số loại băm khác.
Ian Greenleaf Young

2
Tôi đã kiểm tra mã nguồn s3cmd và nó lưu trữ md5 trong siêu dữ liệu khi tải lên. Vì vậy, lệnh này sẽ chỉ in md5 cho các đối tượng được tải lên bằng s3cmd hoặc các đối tượng được tải lên trong một đoạn duy nhất
ZAB

0

Tôi đã kiểm tra chéo jets3t và bảng điều khiển quản lý đối với MD5sum của các tệp được tải lên và ETag dường như bằng MD5sum. Bạn chỉ có thể xem các thuộc tính của tệp trong bảng điều khiển quản lý AWS:

https://console.aws.amazon.com/s3/home


0

Cách dễ nhất là tự đặt tổng kiểm tra làm siêu dữ liệu trước khi bạn tải các tệp này lên nhóm của mình:

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

Giờ đây, bạn có thể truy cập các siêu dữ liệu này mà không cần tải xuống tệp:

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

nguồn: https://github.com/aws/aws-sdk-java/issues/1711


-2

Điều này làm việc cho tôi. Trong PHP, bạn có thể so sánh tổng kiểm tra giữa tệp cục bộ với tệp amazon bằng cách sử dụng:



    // 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

Đây là mã để lấy ETag S3 cho một đối tượng trong PowerShell được chuyển đổi từ c #.

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

Đây là mã để có được băm MD5 theo năm 2017

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

Mã được nhận xét là nơi hầu hết mọi người hiểu sai khi đổi nó thành hex

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.