Вопрос

Я использовал hashlib (который заменяет md5 в Python 2.6/3.0), и он работал нормально, если я открывал файл и помещал его содержимое в hashlib.md5() функция.

Проблема связана с очень большими файлами, размер которых может превышать размер оперативной памяти.

Как получить хэш MD5 файла, не загружая весь файл в память?

Это было полезно?

Решение

Разбейте файл на куски по 128 байт и последовательно передайте их в MD5, используя update().

При этом используется тот факт, что MD5 имеет 128-байтовые блоки дайджеста.В принципе, когда MD5 digest()s файл, это именно то, что он делает.

Если вы убедитесь, что освобождаете память на каждой итерации (т.не читать весь файл в памяти), это должно занимать не более 128 байт памяти.

Одним из примеров является чтение фрагментов следующим образом:

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

Другие советы

Вам нужно прочитать файл кусками подходящего размера:

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

Обновление, приведенное выше, было основано на комментариях Фрериха Раабе. Я протестировал его и обнаружил, что оно корректно при установке Windows Python 2.7.2.

Я перепроверил результаты с помощью инструмента «jacksum».

jacksum -a md5 <filename>

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

если вас интересует более питонический (без «пока 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() нужна пустая строка байтов, чтобы возвращаемый итератор остановился на EOF, поскольку read() возвращает b'' (а не просто '').

Вот моя версия метода @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()

Используя несколько комментариев/ответов в этой теме, вот мое решение:

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()
  • Это «питонический»
  • Это функция
  • Он избегает неявных значений:всегда отдавайте предпочтение явным.
  • Это позволяет (очень важно) оптимизировать производительность.

И наконец,

- Это было создано сообществом, спасибо всем за ваши советы/идеи.

Портативное решение 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() функция с дозорный (пустая строка).

Созданный в этом случае итератор будет вызывать о [лямбда-функция] без аргументов для каждого вызова ее next() метод;если возвращаемое значение равно дозорному, StopIteration будет повышено, в противном случае значение будет возвращено.

Если ваши файлы Действительно big, вам также может потребоваться отобразить информацию о ходе выполнения.Вы можете сделать это, вызвав функцию обратного вызова, которая печатает или записывает количество вычисленных байтов:

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

Ремикс кода Бастьена Семена, в котором учтены комментарии 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

Вы не можете получить md5, не прочитав полное содержимое.но ты можешь использовать обновлять функция для чтения содержимого файлов поблочно.
м.обновление(а);m.update(b) эквивалентен m.update(a+b)

Я думаю, что следующий код более питонический:

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

Реализация принятого ответа для 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()

Я не люблю петли.На основе @Натана Фегера:

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

Я не уверен, что здесь не слишком много суеты.Недавно у меня возникли проблемы с md5 и файлами, хранящимися в виде больших двоичных объектов в MySQL, поэтому я экспериментировал с файлами разных размеров и простым подходом Python, а именно:

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

Я не смог обнаружить заметной разницы в производительности при размерах файлов от 2 КБ до 20 МБ, поэтому нет необходимости «разбивать» хеширование.В любом случае, если Linux придется перенести на диск, он, вероятно, сделает это, по крайней мере, настолько хорошо, насколько способен средний программист удержаться от этого.Оказалось, что проблема не связана с md5.Если вы используете MySQL, не забудьте уже имеющиеся функции md5() и sha1().

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top