Як отримати md5sum файлу на Amazon S3


80

Якщо у мене є файли на Amazon S3, який найпростіший спосіб отримати їх md5sum без необхідності завантажувати файли?

Дякую


1
Заголовок ETag - MD5, але не для багаточастинних файлів. Тут більше інформації про те , як ви можете використовувати його: stackoverflow.com/questions/6591047 / ...
r03

2
Чи немає способу обчислити MD5 для об'єкта S3, не отримавши весь об'єкт і не обчисливши локально? В даний час жодна з відповідей насправді не стосується цього дуже простого питання, а натомість зосереджується виключно на ETag. Більшість відповідей, що пропонують використання ETag, навіть визнають, що він не є відповідною заміною для розрахованого MD5.
bsplosion 05.03.20

Відповіді:


33

Документація AWS ETagговорить:

Тег сутності - це хеш об'єкта. ETag відображає зміни лише щодо вмісту об’єкта, а не його метаданих. ETag може бути або не бути дайджестом MD5 даних об’єкта. Чи це так, залежить від того, як був створений об'єкт і як він зашифрований, як описано нижче:

  • Об'єкти, створені за допомогою об'єкта PUT, об'єкта POST або копіювання, або за допомогою консолі керування AWS і зашифровані за допомогою SSE-S3 або відкритого тексту, мають теги ET, які є дайджестом MD5 даних їх об'єктів.
  • Об'єкти, створені за допомогою об'єкта PUT, об'єкта POST або копіювання, або за допомогою консолі управління AWS і зашифровані за допомогою SSE-C або SSE-KMS, мають теги ET, які не є дайджестом MD5 даних їх об'єктів.
  • Якщо об’єкт створюється за допомогою операції багатозавантажного завантаження або копіювання частин, ETag не є дайджестом MD5, незалежно від методу шифрування.

Посилання: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html


26

Здається, ETag не є MD5 для багатодольних завантажень (згідно з коментарем Гаеля Фрейтера). У цих випадках він містить суфікс мінус і число. Однак навіть біт перед мінусом не здається MD5, хоча він такий же довжини, як MD5. Можливо, суфікс - це кількість завантажених частин?


3
Здається, цей суфікс з’являється лише тоді, коли файл великий (більше 5 Гб). Перевіряючи кілька великих файлів, які я маю, виявляється, що суфікс представляє кількість завантажених частин. Однак перша частина, схоже, не має того самого хешу md5, що і вихідний файл. При обчисленні цього хешу Amazon повинен складати додаткові дані для кожної частини. Я хотів би знати алгоритм, щоб я міг перевірити деякі свої файли.
broc.seib

2
Алгоритм хешування описаний тут: stackoverflow.com/questions/6591047 / ...
Nakedible

@ broc.seib Я бачу суфікс для файлів набагато менших розмірів, таких як 18,3 МБ. Цікаво, чи це залежить від того, що використовується для завантаження файлу; Я використовую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?
Джейсон Лю

Є різні.
li xin

Під час використання etag_checksumфункції я зіткнувся з проблемою з файлами, меншими тоді chunkt_size. Простий тест розміру файлу ( os.path.getsize(filename) < chunk_size) це виправив, якщо у когось ще є така проблема. У такому випадку хешhashlib.md5(f.read())
KingBugAndTheCodeWizard

5

Це дуже давнє запитання, але мені було важко знайти інформацію нижче, і це одне з перших місць, яке я міг знайти, тому я хотів би детально розказати його на випадок, якщо комусь буде потрібно.

ETag - це MD5. Але для файлів, що завантажуються з декількох частин, MD5 обчислюється на основі об’єднання MD5 кожної завантаженої частини. Тому вам не потрібно обчислювати MD5 на сервері. Просто отримайте ETag, і все.

Як сказав у цьому @EmersonFarrugia відповіді:

Скажімо, ви завантажили файл розміром 14 МБ, а розмір вашої частини - 5 МБ. Обчисліть 3 контрольні суми MD5, що відповідають кожній частині, тобто контрольну суму перших 5 МБ, других 5 МБ та останніх 4 МБ. Потім візьміть контрольну суму їх об’єднання. Оскільки контрольні суми MD5 - це шістнадцяткове представлення двійкових даних, просто переконайтеся, що ви взяли MD5 декодованої двійкової конкатенації, а не кодованої ASCII або UTF-8. Після цього додайте дефіс і кількість частин, щоб отримати ETag.

Тож єдине, що вам потрібно, це ETag та розмір частини для завантаження. Але ETag має суфікс -NumberOfParts. Таким чином, ви можете розділити розмір на суфікс і отримати розмір частини. 5 Мб - це мінімальний розмір деталі та значення за замовчуванням. Розмір деталі повинен бути цілим числом, тому ви не можете отримати такі речі, як 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

4

Для тих, хто витрачає час на пошуки, щоб з’ясувати, чому md5 не такий, як ETag у S3.

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

Цей код працює для мене для невеликих файлів. Великі файли дають мені інший хеш
Тоно Нам

скільки розміру файлу?
Pitipong Guntawong

як отримати розмір фрагмента багатокомпонентного ключа об’єкта s3?
Даніель

Залежно від програм для завантаження. Ви можете встановити розмір шматка під час завантаження через AWS CLI. (За замовчуванням: 8 МБ) ref: docs.aws.amazon.com/cli/latest/topic/s3-config.html
Pitipong Guntawong

1

Я виявив, що s3cmd має опцію --list-md5, яку можна використовувати з командою ls, наприклад

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

Сподіваюся, це допомагає.


Це зручно, але, як згадувалося в декількох інших відповідях, для деяких файлів це буде не фактична сума MD5, а інший тип хешу.
Ян Грінліф Янг,

2
Я перевірив вихідний код s3cmd, і він зберігає md5 у метаданих під час завантаження. Так ця команда буде друкувати тільки md5 для об'єктів , завантажених з s3cmd або об'єкти завантажені в один блок
ZAB

0

Я перепроверив jets3t та консоль управління щодо завантажених файлів MD5sum, і ETag, здається, дорівнює MD5sum. Ви можете просто переглянути властивості файлу на консолі керування AWS:

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


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 ви можете порівняти контрольну суму між локальним файлом і файлом Amazon, використовуючи це:



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

Ось код для отримання S3 ETag для об’єкта в PowerShell, перетвореного з 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

Ось код для отримання хешу MD5 відповідно до 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);
    }
}

Коментований код - це те, де більшість людей помиляються, змінюючи його на шістнадцятковий

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.