Obter hash MD5 de arquivos grandes em Python
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?
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>
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.