J'ai utilisé hashlib (qui remplace md5 dans Python 2.6/3.0) et tout s'est bien passé si j'ai ouvert un fichier et mis son contenu dans hashlib.md5()
= fonction.
Le problème vient de très gros fichiers que leur taille pourrait dépasser RAM taille.
Comment obtenir le hachage MD5 d'un fichier sans charger l'intégralité du fichier en mémoire?
Divisez le fichier en morceaux de 128 octets et transmettez-les à MD5 consécutivement à l'aide de update()
.
Cela tire parti du fait que MD5 dispose de blocs de digestion de 128 octets. Fondamentalement, lorsque MD5 digest()
reprend le fichier, c'est exactement ce qu'il fait.
Si vous vous assurez de libérer de la mémoire à chaque itération (c’est-à-dire de ne pas lire le fichier en entier dans la mémoire), la mémoire ne doit pas dépasser 128 octets.
Un exemple est de lire les morceaux comme suit:
f = open(fileName)
while not endOfFile:
f.read(128)
Vous devez lire le fichier en morceaux de taille appropriée:
def md5_for_file(f, block_size=2**20):
md5 = hashlib.md5()
while True:
data = f.read(block_size)
if not data:
break
md5.update(data)
return md5.digest()
NOTE: Assurez-vous d’ouvrir votre fichier avec le 'rb' à l’ouverture - sinon vous obtiendrez un résultat erroné.
Donc, pour faire le tout en une seule méthode - utilisez quelque chose comme:
def generate_file_md5(rootdir, filename, blocksize=2**20):
m = hashlib.md5()
with open( os.path.join(rootdir, filename) , "rb" ) as f:
while True:
buf = f.read(blocksize)
if not buf:
break
m.update( buf )
return m.hexdigest()
La mise à jour ci-dessus était basée sur les commentaires fournis par Frerich Raabe - et je l'ai testé et trouvé correct sur mon Python 2.7.2 installation de Windows
J'ai vérifié les résultats avec l'aide de l'outil "jacksum".
jacksum -a md5 <filename>
si vous vous souciez de plus de Pythonic (no 'While True') façon de lire le fichier, vérifiez ce code:
import hashlib
def checksum_md5(filename):
md5 = hashlib.md5()
with open(filename,'rb') as f:
for chunk in iter(lambda: f.read(8192), b''):
md5.update(chunk)
return md5.digest()
Notez que la fonction iter () a besoin d’une chaîne d’octets vide pour que l’itérateur renvoyé s’arrête à EOF, puisque read () renvoie b '' (pas seulement '').
Voici ma version de la méthode de @Piotr Czapla:
def md5sum(filename):
md5 = hashlib.md5()
with open(filename, 'rb') as f:
for chunk in iter(lambda: f.read(128 * md5.block_size), b''):
md5.update(chunk)
return md5.hexdigest()
En utilisant plusieurs commentaires/réponses dans ce fil de discussion, voici ma solution:
import hashlib
def md5_for_file(path, block_size=256*128, hr=False):
'''
Block size directly depends on the block size of your filesystem
to avoid performances issues
Here I have blocks of 4096 octets (Default NTFS)
'''
md5 = hashlib.md5()
with open(path,'rb') as f:
for chunk in iter(lambda: f.read(block_size), b''):
md5.update(chunk)
if hr:
return md5.hexdigest()
return md5.digest()
Et enfin,
- Ceci a été construit par une communauté, merci à tous pour vos conseils/idées.
A Python 2/3 solution portable
Pour calculer une somme de contrôle (md5, sha1, etc.), vous devez ouvrir le fichier en mode binaire, car vous allez additionner les valeurs d'octets:
Pour être py27/py3 portable, vous devriez utiliser les paquets io
, comme ceci:
import hashlib
import io
def md5sum(src):
md5 = hashlib.md5()
with io.open(src, mode="rb") as fd:
content = fd.read()
md5.update(content)
return md5
Si vos fichiers sont volumineux, vous préférerez peut-être lire le fichier par morceaux afin d'éviter de stocker tout le contenu du fichier en mémoire:
def md5sum(src, length=io.DEFAULT_BUFFER_SIZE):
md5 = hashlib.md5()
with io.open(src, mode="rb") as fd:
for chunk in iter(lambda: fd.read(length), b''):
md5.update(chunk)
return md5
Le truc ici consiste à utiliser la fonction iter()
avec un sentinel (la chaîne vide).
L'itérateur créé dans ce cas appellera o [la fonction lambda] sans argument pour chaque appel de sa méthode
next()
; si la valeur renvoyée est égale à sentinel,StopIteration
sera levé, sinon la valeur sera renvoyée.
Si vos fichiers sont très gros , vous devrez peut-être également afficher des informations sur la progression. Vous pouvez le faire en appelant une fonction de rappel qui affiche ou enregistre la quantité d'octets calculés:
def md5sum(src, callback, length=io.DEFAULT_BUFFER_SIZE):
calculated = 0
md5 = hashlib.md5()
with io.open(src, mode="rb") as fd:
for chunk in iter(lambda: fd.read(length), b''):
md5.update(chunk)
calculated += len(chunk)
callback(calculated)
return md5
n remix de code de Bastien Semene qui prend en compte le commentaire de Hawkwing sur la fonction de hachage générique ...
def hash_for_file(path, algorithm=hashlib.algorithms[0], block_size=256*128, human_readable=True):
"""
Block size directly depends on the block size of your filesystem
to avoid performances issues
Here I have blocks of 4096 octets (Default NTFS)
Linux Ext4 block size
Sudo tune2fs -l /dev/sda5 | grep -i 'block size'
> Block size: 4096
Input:
path: a path
algorithm: an algorithm in hashlib.algorithms
ATM: ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
block_size: a multiple of 128 corresponding to the block size of your filesystem
human_readable: switch between digest() or hexdigest() output, default hexdigest()
Output:
hash
"""
if algorithm not in hashlib.algorithms:
raise NameError('The algorithm "{algorithm}" you specified is '
'not a member of "hashlib.algorithms"'.format(algorithm=algorithm))
hash_algo = hashlib.new(algorithm) # According to hashlib documentation using new()
# will be slower then calling using named
# constructors, ex.: hashlib.md5()
with open(path, 'rb') as f:
for chunk in iter(lambda: f.read(block_size), b''):
hash_algo.update(chunk)
if human_readable:
file_hash = hash_algo.hexdigest()
else:
file_hash = hash_algo.digest()
return file_hash
vous ne pouvez pas obtenir c'est md5 sans lire le contenu complet. mais vous pouvez utiliser la fonction pdate pour lire le contenu des fichiers bloc par bloc.
m.update (a); m.update (b) est équivalent à m.update (a + b)
Je pense que le code suivant est plus Pythonic:
from hashlib import md5
def get_md5(fname):
m = md5()
with open(fname, 'rb') as fp:
for chunk in fp:
m.update(chunk)
return m.hexdigest()