Tips:
下方代码使用了f-string格式化字符串使用Python3.6以下运行时,请修改字符串格式化方法!
文件完整性校验的作用
对于开发的程序,为了防止他人纂改,一般通过哈希算法(md5,sha1,sha256)进行文件的完整性校验
一般校验文件我们会对小型文件和大型文件进行区分
① 如果是小型文件,可以直接通过for循环读取字节流,对所有字节流进行hash加密获取哈希值即可
② 对于大型文件,我们一般会将文件分成多个块
例如: 一个1000字节的文件,分成10块,每次从100,200,300......字节分别读取50字节并连接起来进行哈希计算即可
1. 对于小型文件
本代码仅实现对文件md5值的获取,如果实现密码比较,直接通过布尔运算即可
### 小型文件校验
### 创建两个文本文件,其中第二个文件多了一个字符串8
import hashlib
import os.path
def check_small_file(file_path, enc_type=hashlib.md5()):
"""
该方法会通过for循环以字节流读取文件
并且通过updata方法添加到md5对象中进行校验
如果用于大文件,则会校验很慢
:param file_path 文件路径
:param enc_type 加密方式,传入hashlib方法
"""
enc_obj = enc_type
with open(file_path, 'rb') as f:
for line in f:
enc_obj.update(line)
return enc_obj.hexdigest()
2. 对于大型文件
大型文件我们需要先获取文件的字节数量,获取字节数量有两种方法进行获取
获取后按照我们需要进行分段的个数,进行整除,然后移动文件指针即可实现分段读取
# 大型文件校验
def check_big_file(file_path: str, check_frequency: int = 10, check_once_bytes=100, enc_type=hashlib.md5()):
"""
该方法用于大文件校验
通过seek方法移动文件指针
保证对文件每10%的地方进行一次读取,并校验其完整性!
:param file_path 文件路径
:param check_frequency 校验次数默认10,填写几次则会分个区间去读取并校验
:check_once_bytes 单次校验字节,默认100个字节
:param enc_type 加密方式,传入hashlib方法
:tips 如果文件过小,可能会出现校验出错的文件,所以该方法一般用于大型文件校验
"""
# 获取文件的字节数(方法1)
file_bytes = os.path.getsize(file_path)
enc_obj = enc_type
with open(file_path, 'rb') as f:
# 获取文件的字节数(方法2)
f.seek(0, 2) # 将文件指针移动到末尾
file_bytes = f.tell() # 获取末尾的指针值 -> 字节总数
check_bytes = file_bytes // check_frequency # 每次移动的文件字节数
for i in range(check_frequency):
f.seek(check_bytes * i, 0)
enc_obj.update(f.read(check_once_bytes))
return enc_obj.hexdigest()
3.将上述代码进行整合就可以实现大小文件校验,并且支持多个哈希算法
完整实例如下:
这里就不再写布尔运算了,只要获得加密值就可以了
# coding: utf-8
# @Author: Ruan
# coding:utf-8
### 对于开发后的程序为了防止被人篡改
### 可以使用b模式将文件读取后
### 对文件进行hash处理
### 仅需要对照结果与我们开发时计算的结果是否相同即可
### 并且结果仅需要一串md5值即可
### 小型文件校验
### 创建两个文本文件,其中第二个文件多了一个字符串8
import hashlib
import os.path
def check_small_file(file_path, enc_type=hashlib.md5()):
"""
该方法会通过for循环以字节流读取文件
并且通过updata方法添加到md5对象中进行校验
如果用于大文件,则会校验很慢
:param file_path 文件路径
:param enc_type 加密方式,传入hashlib方法
"""
enc_obj = enc_type
with open(file_path, 'rb') as f:
for line in f:
enc_obj.update(line)
return enc_obj.hexdigest()
def check_big_file(file_path: str, check_frequency: int = 10, check_once_bytes=100, enc_type=hashlib.md5()):
"""
该方法用于大文件校验
通过seek方法移动文件指针
保证对文件每10%的地方进行一次读取,并校验其完整性!
:param file_path 文件路径
:param check_frequency 校验次数默认10,填写几次则会分个区间去读取并校验
:param enc_type 加密方式,传入hashlib方法
:tips 如果文件过小,可能会出现校验出错的文件,所以该方法一般用于大型文件校验
"""
# 获取文件的字节数(方法1)
file_bytes = os.path.getsize(file_path)
enc_obj = enc_type
with open(file_path, 'rb') as f:
# 获取文件的字节数(方法2)
f.seek(0, 2) # 将文件指针移动到末尾
file_bytes = f.tell() # 获取末尾的指针值 -> 字节总数
check_bytes = file_bytes // check_frequency # 每次移动的文件字节数
for i in range(check_frequency):
f.seek(check_bytes * i, 0)
enc_obj.update(f.read(check_once_bytes))
return enc_obj.hexdigest()
if __name__ == '__main__':
initial_file = '.\\files\\原文件.txt'
modify_file = '.\\files\\被修改的文件.txt'
md5_init = check_small_file(initial_file)
md5_modify = check_small_file(modify_file)
print(f'初始文件md5:{md5_init}\n修改后文件md5:{md5_modify}')
# 尽管只多了一个字符串8,但是结果却完全不相同
print(f'使用md5进行节选(大型文件)完整性校验获取的md5值:{check_big_file(initial_file)}')
print(f'使用sha256进行节选(大型文件)完整性校验获取的sha256值:{check_big_file(initial_file,enc_type=hashlib.sha256())}')