我使用过hashlib(它取代了Python 2.6/3.0中的md5),如果我打开一个文件并将其内容放在hashlib.md5()函数中,它工作得很好。
问题是对于非常大的文件,它们的大小可能超过RAM大小。
如何在不将整个文件加载到内存的情况下获得文件的MD5哈希值?
我使用过hashlib(它取代了Python 2.6/3.0中的md5),如果我打开一个文件并将其内容放在hashlib.md5()函数中,它工作得很好。
问题是对于非常大的文件,它们的大小可能超过RAM大小。
如何在不将整个文件加载到内存的情况下获得文件的MD5哈希值?
当前回答
我不喜欢循环。根据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()
其他回答
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
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 (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 "(不仅仅是")。
实现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()
使用多个评论/回答这个问题,以下是我的解决方案:
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式的 这是一个函数 它避免隐式值:总是偏爱显式值。 它允许(非常重要的)性能优化
如果不阅读完整的内容,就无法获得它的md5。但是您可以使用update函数逐块读取文件的内容。
m.update(一个);M.update (b)等价于M.update (a+b)。