Get MD5 hash di file di grandi dimensioni in Python
Domanda
Ho usato hashlib (che sostituisce md5 in Python 2.6 / 3.0) e ha funzionato bene se ho aperto un file e mettere il suo contenuto in funzione hashlib.md5()
.
Il problema è con file molto grandi che le loro dimensioni possano superare la dimensione della RAM.
Come ottenere l'hash MD5 di un file senza caricare l'intero file in memoria?
Soluzione
Suddividere il file in blocchi da 128 byte e nutrirli per MD5 consecutivamente utilizzando update()
.
Questa sfrutta il fatto che MD5 è 128 byte digerire blocchi. Fondamentalmente, quando MD5 digest()
s il file, questo è esattamente quello che sta facendo.
Se assicuratevi di liberare la memoria ad ogni iterazione (vale a dire non leggere l'intero file in memoria), tale partecipazione non più di 128 byte di memoria.
Un esempio è quello di leggere i pezzi in questo modo:
f = open(fileName)
while not endOfFile:
f.read(128)
Altri suggerimenti
Hai bisogno di leggere il file in pezzi di dimensioni adeguate:
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: Assicurarsi che si apre il file con il 'rb' all'aperto - altrimenti si otterrà il risultato sbagliato
.Quindi, per fare il tutto in un metodo - usare qualcosa come:
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()
L'aggiornamento di cui sopra si è basata sulle osservazioni fornite da Frerich Raabe - e ho provato questo e l'ho trovato per essere corretto sulla mia installazione di Python 2.7.2 finestre
I controllo incrociato i risultati utilizzando lo strumento 'jacksum'.
jacksum -a md5 <filename>
se ti interessa di più divinatorio (no 'mentre True') modo di leggere il file di verificare questo codice:
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()
Si noti che l'iter () func ha bisogno di una stringa di byte vuota per l'iteratore tornato a fermare a EOF, dal momento che read () restituisce b '' (non solo '').
Ecco la mia versione del metodo di @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()
L'utilizzo di più di commento / risposte in questa discussione, qui è la mia soluzione:
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()
- Questo è "divinatorio"
- Questa è una funzione
- Evita valori impliciti: sempre preferiscono quelli espliciti .
- Permette (molto importante) prestazioni ottimizzazioni
E, infine,
-. Questo è stato costruito da una comunità, ringrazia tutti per i vostri consigli / idee
un pitone 2/3 soluzione portatile
Per calcolare un checksum (MD5, SHA1, ecc), è necessario aprire il file in modalità binaria, perché si somma bytes valori:
Per essere py27 / PY3 portatile, si dovrebbe utilizzare i pacchetti io
, in questo modo:
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 i file sono grandi, è preferibile leggere il file da blocchi per evitare di memorizzare l'intero contenuto del file in 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
Il trucco è quello di utilizzare la funzione iter()
con un < em> sentinella (la stringa vuota).
L'iteratore creato in questo caso chiamerà o [funzione lambda] senza argomenti per ogni chiamata al suo metodo
next()
; se il valore restituito è pari a sentinella,StopIteration
sarà sollevata, in caso contrario verrà restituito il valore.
Se i file sono davvero grande, potrebbe anche essere necessario per visualizzare le informazioni di avanzamento. Si può fare chiamando una funzione di callback che stampa o registra la quantità di byte calcolati:
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
Un remix del codice di Bastien Semene che prendono commento Hawkwing sulla funzione di hashing generica in considerazione ...
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 non può ottenere è md5 senza contenuto completo in lettura. ma u possibile utilizzare aggiornamento funzione per leggere i file di blocco dei contenuti per blocco.
m.update (a); m.update (b) è equivalente a m.update (a + b)
Credo che il seguente codice è più divinatorio:
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()
Attuazione della risposta accettata per 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()
Non mi piace il loop. Sulla base di @ 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
Non sono sicuro che non ci sia un po 'troppo irritabili qui intorno. Recentemente ho avuto problemi con MD5 e file memorizzati come macchie su MySQL così ho sperimentato con i vari formati di file e l'approccio di Python semplice, vale a dire:
FileHash=hashlib.md5(FileData).hexdigest()
ho potuto rilevare alcuna differenza notevole di prestazioni con una serie di file di dimensioni 2Kb a 20Mb e quindi non c'è bisogno di 'pezzo' l'hashing. In ogni caso, se Linux deve andare sul disco, sarà probabilmente farlo almeno così come la capacità del programmatore medio di tenerlo dal farlo. Come è successo, il problema era niente a che fare con MD5. Se stai usando MySQL, non dimenticate la md5 () e SHA1 () funzioni già lì.