Я использовал hashlib (который заменяет md5 в Python 2.6/3.0), и он отлично работал, если я открыл файл и поместил его содержимое в hashlib.md5()
.
Проблема заключается в очень больших файлах, размер которых может превышать размер ОЗУ.
Как получить хэш MD5 файла без загрузки всего файла в память?
Ответ 1
Разбейте файл на 128-байтовые фрагменты и последовательно доведите их до MD5 с помощью update()
.
Это использует тот факт, что MD5 имеет 128-байтовые блоки дайджеста. В основном, когда MD5 digest()
файл, это именно то, что он делает.
Если вы убедитесь, что вы освобождаете память на каждой итерации (т.е. не читаете весь файл в памяти), это занимает не более 128 байт памяти.
Одним из примеров является чтение таких кусков:
f = open(fileName)
while not endOfFile:
f.read(128)
Ответ 2
Вам нужно прочитать файл в кусках подходящего размера:
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()
ПРИМЕЧАНИЕ. Убедитесь, что вы открыли файл с "rb" в открытое, иначе вы получите неправильный результат.
Итак, чтобы сделать всю партию одним способом - используйте что-то вроде:
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()
Последнее обновление было основано на комментариях, предоставленных Frerich Raabe, - и я проверил это и нашел, что это правильно на моей установке Windows Python 2.7.2
Я перекрестно проверил результаты, используя инструмент "jacksum".
jacksum -a md5 <filename>
http://www.jonelo.de/java/jacksum/
Ответ 3
если вам больше нравится pythonic (no 'while True), способ чтения файла проверить этот код:
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()
Обратите внимание, что для функции iter() func требуется пустая строка байта для возвращенного итератора для остановки в EOF, так как read() возвращает b '' (а не только '').
Ответ 4
Здесь моя версия метода @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()
Ответ 5
Используя несколько комментариев/ответов в этом потоке, вот мое решение:
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()
- Это "pythonic"
- Это функция
- Он избегает неявных значений: всегда предпочитайте явные.
- Это позволяет (очень важно) оптимизацию исполнения.
И наконец,
- это было создано сообществом, благодаря всем вашим советам/идеям.
Ответ 6
Портативное решение Python 2/3
Чтобы вычислить контрольную сумму (md5, sha1 и т.д.), Вы должны открыть файл в двоичном режиме, поскольку вы суммируете байты:
Чтобы быть портативным py27/py3, вы должны использовать пакеты io
, например:
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
Если ваши файлы большие, вы можете предпочесть читать файл кусками, чтобы избежать хранения всего содержимого файла в памяти:
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
Трюк здесь заключается в использовании функции iter()
с дозорным (пустая строка).
Созданный в этом случае итератор будет вызывать o [лямбда-функцию] без аргументов для каждого вызова его next()
методу; если возвращаемое значение равно StopIteration
, StopIteration
будет поднят, в противном случае значение будет возвращено.
Если ваши файлы действительно большие, вам также может потребоваться отобразить информацию о ходе. Вы можете сделать это, вызвав функцию обратного вызова, которая печатает или регистрирует количество вычисленных байтов:
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
Ответ 7
Ремикс кода Bastien Semene, который принимает комментарий Hawkwing о общей функции хеширования...
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
Ответ 8
u не может получить его md5 без чтения полного содержимого. но вы можете использовать функцию update для чтения блока содержимого по блоку.
m.update(а); m.update(b) эквивалентно m.update(a + b)
Ответ 9
Я думаю, что следующий код более питонический:
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()
Ответ 10
Я не люблю циклы. Основано на @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()
Ответ 11
Выполнение принятого ответа для 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()
Ответ 12
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
Ответ 13
Я не уверен, что здесь не слишком много шума. Недавно у меня были проблемы с md5 и файлами, хранящимися как blobs в MySQL, поэтому я экспериментировал с различными размерами файлов и простым подходом Python, а именно:
FileHash=hashlib.md5(FileData).hexdigest()
Я не мог обнаружить заметной разницы в производительности с диапазоном файлов размером от 2 Кбит до 20 Мб, и поэтому не нужно "кушать" хеширование. Во всяком случае, если Linux должен перейти на диск, он, вероятно, сделает это, по крайней мере, так же, как и среднюю способность программиста не допустить этого. Как это случилось, проблема не имела ничего общего с md5. Если вы используете MySQL, не забывайте, что функции md5() и sha1() уже существуют.