[python] Python에서 큰 파일의 MD5 해시 가져 오기

나는 hashlib (Python 2.6 / 3.0에서 md5를 대체 함)를 사용했으며 파일을 열고 내용을 hashlib.md5()작동 시키면 정상적으로 작동했습니다 .

문제는 크기가 RAM 크기를 초과 할 수있는 매우 큰 파일에 있습니다.

전체 파일을 메모리에로드하지 않고 파일의 MD5 해시를 얻는 방법은 무엇입니까?



답변

파일을 8192 바이트 청크 (또는 128 바이트의 다른 배수)로 나누고를 사용하여 MD5에 연속적으로 공급하십시오 update().

이것은 MD5에 128 바이트 다이제스트 블록 (8192는 128 × 64)이 있다는 사실을 이용합니다. 전체 파일을 메모리로 읽지 않기 때문에 8192 바이트 이상의 메모리를 사용하지 않습니다.

Python 3.8 이상에서는 할 수 있습니다

import hashlib
with open("your_filename.txt", "rb") as f:
    file_hash = hashlib.md5()
    while chunk := f.read(8192):
        file_hash.update(chunk)
print(file_hash.digest())
print(file_hash.hexdigest())  # to get a printable str instead of bytes


답변

적절한 크기의 청크로 파일을 읽어야합니다.

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/


답변

아래에 의견의 제안을 통합했습니다. 알 감사합니다!

파이썬 <3.7

import hashlib

def checksum(filename, hash_factory=hashlib.md5, chunk_num_blocks=128):
    h = hash_factory()
    with open(filename,'rb') as f:
        for chunk in iter(lambda: f.read(chunk_num_blocks*h.block_size), b''):
            h.update(chunk)
    return h.digest()

파이썬 3.8 이상

import hashlib

def checksum(filename, hash_factory=hashlib.md5, chunk_num_blocks=128):
    h = hash_factory()
    with open(filename,'rb') as f:
        while chunk := f.read(chunk_num_blocks*h.block_size):
            h.update(chunk)
    return h.digest()

원본 게시물

파일을 읽는 더 pythonic ( ‘truele’없음) 방법에 관심이 있다면 다음 코드를 확인하십시오.

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

read ()가 b ”를 반환하기 때문에 iter () func은 반환 된 반복자가 EOF에서 정지하기 위해 빈 바이트 문자열을 필요로합니다.


답변

@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()센티넬 과 함께 함수 를 사용하는 것입니다 (빈 문자열) .

이 경우에 생성 된 반복자 는 메소드에 대한 각 호출에 대한 인수없이 o [람다 함수]를 호출합니다 next(). 반환 된 값이 센티넬과 같으면 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


답변

일반적인 해싱 함수에 대한 Hawkwing 주석을 고려한 Bastien Semene 코드의 리믹스 …

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