快速文件hash
水一篇2019年的旧文。
最近打算把家里服务器上的文件理一下,想把重复的文件找出来,虽然我已经用了ZFS的dedup,实际占用空间并不会重复,但是还是觉得有必要理一下……
写个程序扫描一遍并不复杂,但是要判断文件是不是重复就比较麻烦,可靠的方法当然是做全文件HASH,但是对一个以T为单位的硬盘来说,这样效率太低了,所以写了一段小代码来做一个快速的HASH。
def get_filemd5(fullpath, filename):
file_size = get_filesize(fullpath, filename)
block_count = int((QUICK_HASH_SIZE + BUFSIZE / 2) / BUFSIZE
if QUICK_HASH_SIZE else file_size / BUFSIZE)
block_size = file_size * 1.0 / block_count if QUICK_HASH_SIZE \
else BUFSIZE
m = hashlib.md5()
with open(os.path.join(fullpath, filename), "rb") as f:
for i in xrange(block_count):
pos = int(i * block_size / MINSIZE) * MINSIZE
f.seek(pos, 0)
buf = f.read(BUFSIZE)
m.update(buf)
remaining = file_size - f.tell()
if remaining > 0:
if QUICK_HASH_SIZE and remaining > BUFSIZE:
f.seek(file_size - BUFSIZE, 0)
remaining = BUFSIZE
buf = f.read(remaining)
m.update(buf)
return m.hexdigest()
HASH算法是直接用MD5,简单方便。其中定义了三个常量:QUICK_HASH_SIZE、BUFSIZE和MINSIZE。
其中MINSIZE定义了对齐的大小,BUFSIZE定义了HASH分块的大小,QUICK_HASH_SIZE则是定义了需要做快速HASH的文件大小阈值。
方法很简单:文件小于QUICK_HASH_SIZE,则做全文件HASH,大于QUICK_HASH_SIZE则按BUFSIZE大小间隔取样,总取样数据大小为QUICK_HASH_SIZE。
每个BUFSIZE块在文件中的分布为均匀的,但是每块又以MINSIZE对齐,另外,因为通常文件最经常被修改的部分是最后的部分,所以最后一块一定是与文件结尾对齐,以保证HASH部分包括文件的结尾。
当然,因为没有对全文件做HASH,所以没有被HASH到的部分如果有修改,是不会被发现的,但是对我的需求来说基本不会有这种情况,所以不要紧,但是这样可以让HASH速度大大加快,特别是对那些几个G的大文件(不许联想,我说的是操作系统的安装ISO镜像文件之类)。
推送到[go4pro.org]