Obtenir MD5 hachage de gros fichiers en Python
j'ai utilisé hashlib (qui remplace md5 en Python 2.6/3.0) et ça a bien fonctionné si j'ai ouvert un fichier et mis son contenu dans hashlib.md5()
fonction.
le problème est avec de très gros fichiers que leurs tailles pourraient dépasser la taille de la RAM.
comment obtenir le hachage MD5 d'un fichier sans charger l'ensemble du fichier en mémoire?
11 réponses
briser le fichier en morceaux de 128 octets et les transmettre à MD5 en utilisant update()
.
cela profite du fait que MD5 dispose de blocs de 128 octets digest. En gros, quand MD5 digest()
s le fichier, c'est exactement ce qu'il fait.
si vous vous assurez que vous libérez la mémoire sur chaque itération (c.-à-d. ne pas lire le fichier entier en mémoire), cela ne prendra pas plus de 128 octets de mémoire.
un exemple est pour lire les morceaux comme:
f = open(fileName)
while not endOfFile:
f.read(128)
Vous avez besoin de 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 le mauvais résultat.
donc pour faire le lot entier dans une méthode-utiliser 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 j'ai trouvé qu'elle était correcte sur mon installation Windows Python 2.7.2
j'ai vérifié les résultats à l'aide de la "jacksum' outil.
jacksum -a md5 <filename>
si vous vous souciez de plus de pythonic (no 'while True') façon de lire le fichier vérifier 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 l'iter () func a besoin d'une chaîne d'octets vide pour que l'itérateur retourné 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 thread, 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()
- C'est "pythonic "
- C'est une fonction
- il évite les valeurs implicites: il préfère toujours les valeurs explicites.
- il permet des optimisations de performances (très importantes)
et enfin,
- elle a été construite par une communauté, merci à tous pour vos conseils/idées.
a Python portable 2/3 solution
pour calculer un total de contrôle (md5, sha1, etc.), vous devez ouvrir le fichier en mode binaire, parce que vous ferez la somme des valeurs des octets:
pour être py27/py3 portable, vous devez 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 pouvez préférer lire le fichier par morceaux pour é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
L'astuce ici est d'utiliser le iter()
fonction sentinelle (la chaîne vide).
l'itérateur créé dans ce cas appellera o [la fonction lambda] sans aucun argument pour chaque appel à sa méthode
next()
; si la valeur retournée est égale à sentinel,StopIteration
sera augmentée, sinon la valeur sera retournée.
si vos fichiers sont vraiment grand, vous pouvez également avoir besoin d'Afficher des informations de progrès. Vous pouvez faire cela en appelant une fonction de rappel qui imprime 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
Un remix de Bastien Semene code que prendre Hawkwing commentaire à propos de génériques fonction de hachage en considération...
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
la mise en Œuvre de accepté de répondre pour Django:
import hashlib
from django.db import models
class MyModel(models.Model):
file = models.FileField() # any field based on django.core.files.File
def get_hash(self):
hash = hashlib.md5()
for chunk in self.file.chunks(chunk_size=8192):
hash.update(chunk)
return hash.hexdigest()
import hashlib,re
opened = open('/home/parrot/pass.txt','r')
opened = open.readlines()
for i in opened:
strip1 = i.strip('\n')
hash_object = hashlib.md5(strip1.encode())
hash2 = hash_object.hexdigest()
print hash2
Je ne suis pas sûr qu'il n'y ait pas trop d'agitation par ici. J'ai récemment eu des problèmes avec md5 et des fichiers stockés sous forme de blobs sur MySQL, donc j'ai expérimenté différentes tailles de fichiers et l'approche Python simple, à savoir:
FileHash=hashlib.md5(FileData).hexdigest()
Je ne pouvais détecter aucune différence de performance notable avec une gamme de tailles de fichiers de 2Kb à 20Mb et donc pas besoin de "fracasser" le hachage. Quoi qu'il en soit, si Linux doit aller sur le disque, il le fera probablement au moins aussi bien que le programmeur moyen de la capacité pour l'empêcher de le faire. Le problème n'avait rien à voir avec md5. Si vous utilisez MySQL, n'oubliez pas les fonctions md5() et sha1() déjà présentes.