Pregunta

He utilizado hashlib (que sustituye MD5 en Python 2.6 / 3.0) y funcionó bien si abría un archivo y poner su contenido en función hashlib.md5() .

El problema es con archivos muy grandes que su tamaño pudieran superar el tamaño de RAM.

¿Cómo obtener el hash MD5 de un archivo sin tener que cargar todo el archivo a la memoria?

¿Fue útil?

Solución

Romper el archivo en trozos de 128 bytes y alimentarlos a MD5 consecutivamente usando update().

Esto se aprovecha del hecho de que MD5 tiene 128 bytes a digerir los bloques. Básicamente, cuando MD5 digest()s el archivo, esto es exactamente lo que está haciendo.

Si se asegura a liberar la memoria en cada iteración (es decir, no leer todo el archivo a la memoria), esto tomará no más de 128 bytes de memoria.

Un ejemplo es leer los trozos de este modo:

f = open(fileName)
while not endOfFile:
    f.read(128)

Otros consejos

Es necesario leer el archivo en trozos de tamaño adecuado:

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()

NOTA: Asegúrese de abrir el archivo con el 'rb' a la intemperie - de lo contrario obtendrá un resultado erróneo

.

Así que hacer todo el lote en un método - usar algo como:

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 actualización anterior se basó en las observaciones formuladas por Frerich Raabe - y Probé esto y nos pareció que ser correcto en mi instalación de Python 2.7.2 ventanas

I cotejará los resultados utilizando la herramienta 'Jacksum'.

jacksum -a md5 <filename>

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

si se preocupan más Pythonic (no 'while True') forma de leer el archivo de comprobar este código:

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()

Tenga en cuenta que el iter () func necesita una cadena de bytes vacía para el iterador vuelto a detener al EOF, ya que read () devuelve b '' (no sólo '').

Esta es mi versión del método 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()

El uso de múltiples comentarios / respuestas en este tema, aquí está mi solución:

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()
  • Se trata de "Pythonic"
  • Se trata de una función
  • Evita valores implícitos: siempre se prefieren los explícitos
  • .
  • Permite (muy importante) performances optimizaciones

Y, por último,

-. Esto ha sido construido por una comunidad, gracias a todos por sus consejos / ideas

A Python 2/3 solución portátil

Para calcular una suma de comprobación (MD5, SHA1, etc.), debe abrir el archivo en modo binario, porque vas a sumar valores bytes:

Para ser py27 / AP3 portátil, usted debe utilizar los paquetes io, como esto:

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 los archivos son grandes, es posible que prefiera para leer el archivo por trozos para evitar almacenar todo el contenido del archivo en la memoria:

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

El truco aquí es usar la función iter() con un < em> centinela (la cadena vacía).

  

El iterador creada en este caso llamará o [la función lambda] sin argumentos para cada llamada a su método next(); si el valor devuelto es igual a centinela, será levantado StopIteration, de lo contrario será devuelto el valor.

Si los archivos son realmente grande, es posible que también es necesario para mostrar información de progreso. Puede hacerlo llamando a una función de devolución de llamada que imprime o registra la cantidad de bytes calculados:

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

Una remezcla de código Bastien Semene que tome comentario Hawkwing acerca de la función hash genérico en cuenta ...

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

u no puede conseguirlo es MD5 sin contenido completo de lectura. pero u puede utilizar actualización función para leer los archivos de bloque de contenido por bloque.
m.update (a); m.update (b) es equivalente a m.update (a + b)

Creo que el siguiente código es más 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()

Implementación de respuesta aceptada para 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()

No me gustan los bucles. Basado en @ 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()
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

No estoy seguro de que no es un poco demasiado alboroto por aquí. Recientemente he tenido problemas con MD5 y archivos almacenados como manchas en MySQL por lo que experimentaron con diferentes tamaños de archivo y el enfoque Python sencilla, a saber:

FileHash=hashlib.md5(FileData).hexdigest()

pude detectar ninguna diferencia notable en el rendimiento con una amplia gama de tamaños de archivo 2Kb a 20Mb y por lo tanto no hay necesidad de 'trozo' el hash. De todos modos, si Linux tiene que ir en el disco, es probable que lo haga al menos tan bien como la capacidad del programador medio para evitar que hacerlo. Como sucedió, el problema había nada que ver con MD5. Si estás usando MySQL, no se olvide el MD5 () y sha1 () funciones ya allí.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top