使用 Python 获取大文件的 MD5 哈希值

2025-02-07 08:44:00
admin
原创
65
摘要:问题描述:我已经使用过hashlib(它取代了Python 2.6/3.0 中的md5),如果我打开一个文件并将其内容放入函数中,它就能正常工作hashlib.md5()。问题在于,如果文件非常大,其大小可能会超出 RAM 大小。如何在不将整个文件加载到内存中的情况下获取文件的 MD5 哈希值?解决方案 1:...

问题描述:

我已经使用过hashlib(它取代了Python 2.6/3.0 中的md5),如果我打开一个文件并将其内容放入函数中,它就能正常工作hashlib.md5()

问题在于,如果文件非常大,其大小可能会超出 RAM 大小。

如何在不将整个文件加载到内存中的情况下获取文件的 MD5 哈希值?


解决方案 1:

您需要以适当大小的块读取文件:

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>

解决方案 2:

将文件分成 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

解决方案 3:

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

Python 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(无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''`''

解决方案 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 等),您必须以二进制模式打开文件,因为您将对字节值求和:

为了实现 Python 2.7 和 Python 3 的可移植性,您应该使用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 [lambda 函数] next();如果返回的值等于 sentinel,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:

如果不读取全部内容,则无法获取其 md5。但可以使用更新功能逐块读取文件内容。

m.update(a); m.update(b)等同于m.update(a+b)

解决方案 9:

我认为下面的代码更加符合Python 风格:

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:

Yuval Adam对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()

解决方案 11:

我不喜欢循环。根据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()

解决方案 12:

正如@pseyfert的评论中提到的那样;在Python 3.11及更高版本中可以使用。虽然没有明确记录,但从其源代码(第230-236行)hashlib.file_digest()可以看出,该函数内部使用了一种类似于已接受答案中的分块方法。

该函数还提供了一个仅限关键字的参数_bufsize,其默认值为 2^18 = 262,144 字节,用于控制分块的缓冲区大小;但是,考虑到其前导下划线和缺少的文档,它可能应该被视为一个实现细节。

无论如何,以下代码等效地重现了 Python 3.11+ 中接受的答案(除了不同的块大小):

import hashlib
with open("your_filename.txt", "rb") as f:
    file_hash = hashlib.file_digest(f, "md5")  # or `hashlib.md5` as 2nd arg
print(file_hash.digest())
print(file_hash.hexdigest())  # to get a printable str instead of bytes

解决方案 13:

我不确定这里是否还有太多麻烦。我最近遇到了 md5 和 MySQL 中存储为 blob 的文件的问题,因此我尝试了各种文件大小和简单的 Python 方法,即:

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

对于 2 KB 到 20 MB 的文件大小,我没有发现任何明显的性能差异,因此不需要“分块”散列。无论如何,如果 Linux 必须转到磁盘,它至少会像普通程序员阻止它这样做一样好地完成它。事实上,问题与 md5 无关。如果您使用的是 MySQL,请不要忘记已经存在的 md5() 和 sha1() 函数。

解决方案 14:

import hashlib,re
opened = open('/home/parrot/pass.txt','r')
opened = open.readlines()
for i in opened:
    strip1 = i.strip('
')
    hash_object = hashlib.md5(strip1.encode())
    hash2 = hash_object.hexdigest()
    print hash2
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用