Pergunta

Eu tenho hashlib usado (que substitui md5 em Python 2.6 / 3.0) e funcionou muito bem se eu abrisse um arquivo e colocar seu conteúdo em hashlib.md5() função.

O problema é com arquivos muito grandes que seus tamanhos podem exceder o tamanho RAM.

Como obter o hash MD5 de um arquivo sem carregar o arquivo inteiro na memória?

Foi útil?

Solução

quebrar o arquivo em pedaços de 128 bytes e alimentá-los para MD5 consecutivamente usando update().

Este aproveita o fato de que MD5 tem 128 bytes digerir blocos. Basicamente, quando MD5 digest()s o arquivo, esse é exatamente o que está fazendo.

Se você certifique-se de liberar a memória em cada iteração (ou seja, não ler o arquivo inteiro para a memória), isso não mais demorar mais de 128 bytes de memória.

Um exemplo é ler os pedaços assim:

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

Outras dicas

Você precisa ler o arquivo em pedaços de tamanho adequado:

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: Certifique-se de abrir o arquivo com o 'rb' para a céu aberto - caso contrário, você vai obter o resultado errado

.

Assim, para fazer todo o lote em um 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()

A atualização acima foi baseada nos comentários fornecidos por Frerich Raabe - e eu testei isso e achei que fosse correto na minha instalação do Python 2.7.2 janelas

Eu cruzo-verificou os resultados utilizando a ferramenta 'jacksum'.

jacksum -a md5 <filename>

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

Se você se preocupa com mais Python (não 'while True') maneira de ler o arquivo de verificar 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()

Note que a func iter () precisa de um byte string vazia para o iterador voltou a parada em EOF, desde read () retorna b '' (e não apenas '').

Aqui está a minha versão do 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()

Usando vários comentário / respostas neste tópico, aqui está a minha solução:

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()
  • Este é "pythônico"
  • Esta é uma função
  • Evita valores implícitos:. Prefira sempre os explícitos
  • Permite (muito importante) performances otimizações

E, finalmente,

-. Esta foi construída por uma comunidade, agradece a todos por seus conselhos / ideias

A Python 2/3 solução portátil

Para calcular uma soma de verificação (md5, sha1, etc.), é necessário abrir o arquivo em modo binário, porque você vai resumir bytes valores:

Para ser py27 / PY3 portátil, você deve usar os pacotes io, como este:

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

Se os arquivos são grandes, você pode preferir ler o arquivo por pedaços para evitar armazenar todo o conteúdo do arquivo na memória:

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

O truque aqui é usar a função iter() com um < em> sentinela (a cadeia vazia).

O iterador criado neste caso vai chamar o [a função lambda] sem argumentos para cada chamada ao seu método next(); Se o valor devolvido é igual a sentinela, StopIteration será levantada, caso contrário, o valor será devolvido.

Se os arquivos são realmente grande, você também pode precisar de informações sobre o progresso de exibição. Você pode fazer isso chamando uma função de retorno de chamada que imprime ou registra a quantidade 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

Um remix do código Bastien Semene que levam Hawkwing comentário sobre a função hash genérico em consideração ...

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 não pode obtê-lo de md5 sem conteúdo completo ler. mas u pode usar atualização para ler os arquivos de bloco de conteúdo por bloco.
m.update (a); m.update (b) é equivalente a m.update (a + b)

Eu acho que o seguinte código é mais Python:

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

Implementação de resposta aceito 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()

Eu não gosto de loops. Baseado em @ 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

Eu não tenho certeza de que não é um pouco demais agitação por aqui. Recentemente, tive problemas com md5 e arquivos armazenados como bolhas no MySQL, então eu experimentei com vários tamanhos de arquivo ea abordagem Python simples, a saber:

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

Eu poderia detectar nenhuma diferença de desempenho notável com uma gama de tamanhos de arquivo 2Kb de 20Mb e, portanto, não há necessidade de 'pedaço' do hashing. De qualquer forma, se o Linux tem que ir para o disco, ele provavelmente irá fazê-lo, pelo menos, bem como a capacidade do programador médio para mantê-lo de fazê-lo. Como isso aconteceu, o problema era nada a ver com md5. Se você estiver usando MySQL, não se esqueça do md5 () e sha1 () funções já existe.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top