104 votes

Quel est l'algorithme permettant de calculer l'Etag Amazon-S3 pour un fichier de plus de 5 Go ?

Les fichiers téléchargés sur Amazon S3 qui sont inférieurs à 5 Go ont un ETag qui est simplement le hachage MD5 du fichier, ce qui permet de vérifier facilement si vos fichiers locaux sont les mêmes que ceux que vous mettez sur S3.

Mais si votre fichier est supérieur à 5 Go, Amazon calcule l'ETag différemment.

Par exemple, j'ai effectué un téléchargement multipart d'un fichier de 5 970 150 664 octets en 380 parties. Maintenant, S3 montre qu'il a un ETag de 6bcf86bed8807b8e78f0fc6e0a53079d-380 . Mon fichier local a un hash md5 de 702242d3703818ddefe6bf7da2bed757 . Je pense que le nombre après le tiret est le nombre de pièces dans le téléchargement en plusieurs parties.

Je soupçonne également que le nouvel ETag (avant le tiret) est toujours un hachage MD5, mais avec des métadonnées incluses en cours de route à partir du téléchargement multipart.

Quelqu'un sait-il comment calculer l'ETag en utilisant le même algorithme qu'Amazon S3 ?

111voto

Emerson Farrugia Points 3085

Supposons que vous ayez téléchargé un fichier de 14 Mo dans un seau sans cryptage côté serveur, et que la taille de votre pièce soit de 5 Mo. Calculez 3 sommes de contrôle MD5 correspondant à chaque partie, c'est-à-dire la somme de contrôle du premier 5MB, du deuxième 5MB et du dernier 4MB. Puis prenez la somme de contrôle de leur concaténation. Les sommes de contrôle MD5 sont souvent imprimées sous forme de représentations hexadécimales de données binaires. Veillez donc à prendre la somme de contrôle MD5 de la concaténation binaire décodée, et non de la concaténation codée en ASCII ou UTF-8. Lorsque cela est fait, ajoutez un trait d'union et le nombre de parties pour obtenir l'ETag.

Voici les commandes pour le faire sur Mac OS X à partir de la console :

$ dd bs=1m count=5 skip=0 if=someFile | md5 >>checksums.txt
5+0 records in
5+0 records out
5242880 bytes transferred in 0.019611 secs (267345449 bytes/sec)
$ dd bs=1m count=5 skip=5 if=someFile | md5 >>checksums.txt
5+0 records in
5+0 records out
5242880 bytes transferred in 0.019182 secs (273323380 bytes/sec)
$ dd bs=1m count=5 skip=10 if=someFile | md5 >>checksums.txt
2+1 records in
2+1 records out
2599812 bytes transferred in 0.011112 secs (233964895 bytes/sec)

À ce stade, toutes les sommes de contrôle sont dans checksums.txt . Pour les concaténer et décoder l'hexagone et obtenir la somme de contrôle MD5 du lot, il suffit d'utiliser

$ xxd -r -p checksums.txt | md5

Et maintenant ajoutez "-3" pour obtenir l'ETag, puisqu'il y avait 3 parties.

Notes

  • Si vous avez téléchargé avec aws-cli via aws s3 cp alors vous avez très probablement un chunksize de 8MB. Selon le docs c'est la valeur par défaut.
  • Si le chiffrement côté serveur (SSE) est activé pour le seau, l'ETag ne sera pas la somme de contrôle MD5 (cf. la documentation de l'API ). Mais si vous essayez simplement de vérifier qu'une pièce téléchargée correspond à ce que vous avez envoyé, vous pouvez utiliser la fonction Content-MD5 l'en-tête et S3 le comparera pour vous .
  • md5 sur macOS écrit juste la somme de contrôle, mais md5sum sur Linux/brew affiche également le nom du fichier. Vous devrez le dépouiller, mais je suis sûr qu'il y a une option pour ne sortir que les sommes de contrôle. Vous n'avez pas besoin de vous soucier des espaces car xxd l'ignorera.

Liens vers les codes

31voto

zsero Points 2578

En me basant sur les réponses données ici, j'ai écrit une implémentation Python qui calcule correctement les ETags des fichiers mono et multi-parties.

def calculate_s3_etag(file_path, chunk_size=8 * 1024 * 1024):
    md5s = []

    with open(file_path, 'rb') as fp:
        while True:
            data = fp.read(chunk_size)
            if not data:
                break
            md5s.append(hashlib.md5(data))

    if len(md5s) < 1:
        return '"{}"'.format(hashlib.md5().hexdigest())

    if len(md5s) == 1:
        return '"{}"'.format(md5s[0].hexdigest())

    digests = b''.join(m.digest() for m in md5s)
    digests_md5 = hashlib.md5(digests)
    return '"{}-{}"'.format(digests_md5.hexdigest(), len(md5s))

La taille par défaut de chunk_size est de 8 Mo, utilisée par le système officiel de gestion des données. aws cli et il permet le téléchargement en plusieurs parties de plus de 2 morceaux. Il devrait fonctionner sous Python 2 et 3.

12voto

tlastowka Points 702

implémentation de bash

implémentation python

L'algorithme est littéralement (copié du readme dans l'implémentation python) :

  1. md5 les chunks
  2. regrouper les chaînes md5 ensemble
  3. convertir le glob en binaire
  4. md5 le binaire du chunk global md5s
  5. ajouter "-Number_of_chunks" à la fin de la chaîne md5 du binaire

11voto

Hans Points 71

Voici encore une autre pièce du puzzle du défi AWS.

Pour information, cette réponse suppose que vous avez déjà compris comment calculer le "MD5 des parties MD5" et que vous pouvez reconstruire votre ETag multipartite AWS à partir de toutes les autres réponses déjà fournies ici.

Cette réponse répond à l'inconvénient d'avoir à "deviner" ou à "deviner" la taille de la pièce téléchargée à l'origine.

Nous utilisons plusieurs outils différents pour le téléchargement vers S3 et ils semblent tous avoir des tailles de parties à télécharger différentes, donc "deviner" n'était pas vraiment une option. De plus, nous avons beaucoup de fichiers qui ont été téléchargés par le passé lorsque la taille des parties semblait être différente. Par ailleurs, la vieille astuce consistant à utiliser une copie de serveur interne pour forcer la création d'un ETag de type MD5 ne fonctionne plus non plus, car AWS a modifié ses copies de serveur interne pour qu'elles utilisent également le multipart (mais avec une taille de partie assez importante).

Alors... Comment pouvez-vous déterminer la taille de la pièce de l'objet ?

Si vous effectuez d'abord une requête head_object et détectez que l'ETag est un ETag de type multipart (incluant un '-<partcount>' à la fin), vous pouvez effectuer une autre requête head_object, mais avec un attribut part_number supplémentaire de 1 (la première partie). Cette requête head_object suivante vous renverra alors le content_length de la première partie. Et voilà... Vous connaissez maintenant la taille de la partie qui a été utilisée et vous pouvez utiliser cette taille pour recréer votre ETag local qui devrait correspondre à l'ETag S3 original créé lors du téléchargement de l'objet.

De plus, si vous voulez être précis (peut-être que certains téléchargements en plusieurs parties utilisent des tailles de parties variables), vous pouvez continuer à appeler les requêtes head_object avec chaque numéro de partie spécifié et calculer le MD5 de chaque partie à partir de la longueur du contenu des parties retournées.

J'espère que cela vous aidera...

10voto

John Meyer Points 111

Pas sûr que ça puisse aider :

Nous sommes actuellement en train de faire un hack moche (mais jusqu'à présent utile) pour fixer ceux mauvais ETags dans les fichiers téléchargés en plusieurs parties, qui consiste à appliquer une modification au fichier dans le seau ; cela déclenche un recalcul md5 d'Amazon qui modifie l'ETag pour qu'il corresponde à la signature md5 réelle.

Dans notre cas :

Fichier : bucket/Foo.mpg.gpg

  1. ETag obtenu : "3f92dffef0a11d175e60fb8b958b4e6e-2"
  2. Faites quelque chose avec le fichier ( le renommer ajouter une méta-donnée comme un faux en-tête, entre autres)
  3. Etag obtenu : "c1d903ca1bb6dc68778ef21e74cc15b0"

Nous ne connaissons pas l'algorithme, mais comme nous pouvons "réparer" l'ETag, nous n'avons pas à nous en préoccuper non plus.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X