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

我已经使用hashlib(在Python 2.6 / 3.0中replacemd5),如果我打开一个文件,并将其内容放在hashlib.md5()函数,它工作得很好。

问题是非常大的文件,它们的大小可能会超过RAM大小。

如何获取文件的MD5哈希值,而不将整个文件加载到内存?

将文件分解成128字节的块,然后使用update()将它们连续提供给MD5。

这利用了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提供的注释 – 我testing了这一点,发现它在我的Python 2.7.2窗口安装中是正确的

我使用'jacksum'工具交叉检查结果。

 jacksum -a md5 <filename> 

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

如果你关心更多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()函数需要一个空的字节string,以便返回的迭代器在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() 

在这个线程中使用多个评论/答案,这里是我的解决scheme:

 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”
  • 这是一个function
  • 它避免了隐含的值:总是喜欢明确的值。
  • 它允许(非常重要)性能优化

最后,

– 这是由社区build立的,感谢您的build议/想法。

一个Python 2/3便携式解决scheme

要计算校验和(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()函数和一个标记 (空string)。

在这种情况下创build的迭代器将为每个对其next()方法的调用调用o [lambda函数] 如果返回的值等于sentinel,则会StopIteration ,否则返回值。

如果您的文件非常大,您可能还需要显示进度信息。 您可以通过调用一个callback函数来打印或logging计算字节的数量:

 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 

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 

没有阅读全部内容,你不能得到它的MD5。 但你可以使用更新function逐块读取文件内容。
m.update(一); m.update(b)相当于m.update(a + b)

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和MySQL上存储为blob的文件的问题,所以我尝试了各种文件大小和直接的Python方法,即:

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

我可以检测到没有明显的性能差异,文件大小为2Kb到20Mb,因此不需要“散列”散列。 无论如何,如果Linux必须进入磁盘,至less和一般程序员防止这种情况的能力一样。 事情发生了,这个问题与md5无关。 如果你正在使用MySQL,不要忘记已经存在的md5()和sha1()函数。