J'ai utilisé hashlib (qui remplace md5 dans Python 2.6 / 3.0) et cela fonctionnait bien si j'ouvrais un fichier et mettais son contenu en hashlib.md5()
fonction.
Le problème est que les très gros fichiers peuvent dépasser la taille de la RAM.
Comment obtenir le hachage MD5 d'un fichier sans charger le fichier entier en mémoire?
Réponses:
Divisez le fichier en morceaux de 8192 octets (ou un autre multiple de 128 octets) et transmettez-les à MD5 consécutivement en utilisant
update()
.Cela tire parti du fait que MD5 a des blocs de résumé de 128 octets (8192 est 128 × 64). Puisque vous ne lisez pas l'intégralité du fichier en mémoire, cela n'utilisera pas beaucoup plus de 8192 octets de mémoire.
Dans Python 3.8+, vous pouvez faire
import hashlib with open("your_filename.txt", "rb") as f: file_hash = hashlib.md5() while chunk := f.read(8192): file_hash.update(chunk) print(file_hash.digest()) print(file_hash.hexdigest()) # to get a printable str instead of bytes
la source
hashlib.blake2b
place demd5
. Contrairement à MD5, BLAKE2 est sécurisé et encore plus rapide.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()
REMARQUE: assurez-vous d'ouvrir votre fichier avec le «rb» à l'ouverture - sinon vous obtiendrez le mauvais résultat.
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 j'ai testé cela et je l'ai trouvé correct sur mon installation Windows Python 2.7.2
J'ai vérifié les résultats en utilisant l'outil «jacksum».
http://www.jonelo.de/java/jacksum/
la source
rb
à laopen
fonction.hexdigest
place dedigest
produira un hachage hexadécimal qui "ressemble" à la plupart des exemples de hachages.if len(data) < block_size: break
?open
toujours un nouveau descripteur de fichier avec la position définie au début du fichier (sauf si vous ouvrez un fichier à ajouter).Ci-dessous, j'ai incorporé la suggestion des commentaires. Merci à tous!
python <3,7
import hashlib def checksum(filename, hash_factory=hashlib.md5, chunk_num_blocks=128): h = hash_factory() with open(filename,'rb') as f: for chunk in iter(lambda: f.read(chunk_num_blocks*h.block_size), b''): h.update(chunk) return h.digest()
python 3.8 et supérieur
import hashlib def checksum(filename, hash_factory=hashlib.md5, chunk_num_blocks=128): h = hash_factory() with open(filename,'rb') as f: while chunk := f.read(chunk_num_blocks*h.block_size): h.update(chunk) return h.digest()
message original
si vous vous souciez d'une manière plus pythonique (pas de `` while True '') 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 retourné s'arrête à EOF, puisque read () renvoie b '' (pas seulement '').
la source
128*md5.block_size
place de8192
.md5.block_size
.b''
syntaxe était nouvelle pour moi. Expliqué ici .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()
la source
En utilisant plusieurs commentaires / réponses dans ce fil, 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.
la source
Une solution portable Python 2/3
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 portable py27 / py3, vous devez utiliser les
io
packages, 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 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 la
iter()
fonction avec une sentinelle (la chaîne vide).Si vos fichiers sont vraiment volumineux, vous devrez peut-être également afficher des informations de progression. Vous pouvez le faire 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
la source
Un remix du code 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
la source
Vous ne pouvez pas obtenir son md5 sans lire le contenu complet. mais vous pouvez utiliser la fonction de mise à jour pour lire le contenu des fichiers bloc par bloc.
m.update (a); m.update (b) équivaut à m.update (a + b)
la source
Je pense que le code suivant est plus pythonique:
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()
la source
Je n'aime pas les boucles. Basé sur @Nathan Feger:
md5 = hashlib.md5() with open(filename, 'rb') as f: functools.reduce(lambda _, c: md5.update(c), iter(lambda: f.read(md5.block_size * 128), b''), None) md5.hexdigest()
la source
hashlib
API de s ne fonctionne pas vraiment bien avec le reste de Python. Par exemple, prenonsshutil.copyfileobj
ce qui échoue de près. Ma prochaine idée étaitfold
(akareduce
) de plier les itérables ensemble en objets uniques. Comme par exemple un hachage.hashlib
ne fournit pas d'opérateurs, ce qui rend cela un peu compliqué. Néanmoins, nous plions un iterables ici.Implémentation de la réponse acceptée 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()
la source
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
la source
Je ne suis pas sûr qu'il n'y ait pas trop de tracas ici. J'ai récemment eu des problèmes avec md5 et des fichiers stockés sous forme de blobs sur MySQL, alors j'ai expérimenté différentes tailles de fichiers et l'approche simple de Python, à savoir:
Je n'ai pu détecter aucune différence de performance notable avec une plage de tailles de fichiers allant de 2 Ko à 20 Mo et donc pas besoin de «fragmenter» le hachage. Quoi qu'il en soit, si Linux doit aller sur disque, il le fera probablement au moins aussi bien que la capacité du programmeur moyen de l'empêcher de le faire. En fait, 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.
la source