Получить MD5 хэш больших файлов в Python

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

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

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

11 ответов


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

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

Если вы убедитесь, что освобождаете память на каждой итерации (т. е. не читаете весь файл в память), это займет не более 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()

обновление выше было основано на комментариях, предоставленных Frerich Raabe - и я проверил это и нашел его правильным на моей установке Python 2.7.2 windows

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

jacksum -a md5 <filename>

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


Если вы заботитесь о более 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() нуждается в пустой строке байта для остановки возвращаемого итератора в 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()


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

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


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

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 и файлами, хранящимися как blobs на MySQL, поэтому я экспериментировал с различными размерами файлов и простым подходом Python, а именно:

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

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