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?

170
demandé sur Chris 2009-07-15 16:52:07

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)
136
répondu Yuval Adam 2009-07-15 14:40:55

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>

http://www.jonelo.de/java/jacksum /

203
répondu TheDoctor 2015-12-07 19:50:35

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 ").

98
répondu Piotr Czapla 2014-06-13 20:49:51

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()
46
répondu Nathan Feger 2014-06-03 21:02:33

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.

28
répondu Bastien Semene 2013-08-27 08:16:14

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
6
répondu Laurent LAPORTE 2016-12-04 17:41:50

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
4
répondu Richard 2015-07-07 20:43:07

vous ne pouvez pas obtenir son md5 sans lire le contenu complet. mais u peut utiliser la fonction update pour lire le contenu des fichiers bloc par bloc.

M. mise à jour (a); M. la mise à jour (b) est équivalente à M. update (a+b)

1
répondu sunqiang 2009-07-15 12:55:58

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()
-1
répondu lampslave 2016-08-02 11:22:09
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
-2
répondu mhmad msarwe 2016-08-27 20:49:26

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.

-4
répondu user2099484 2015-04-04 12:50:09