Python 文件校验实战:分块计算 SHA-256,检查下载是否完整
下载压缩包、模型文件或备份后,仅看到“下载完成”还不足以确认内容与发布者提供的文件一致。如果发布页面提供了 SHA-256 校验值,就可以在本地重新计算并比较。本文用 Python 标准库实现一个小工具,重点说明分块读取、二进制模式和校验结果的边界。
一、为什么要分块读取
直接把整个文件读进内存,写法很短,但大文件会占用大量内存。分块方案每次读取 1 MiB,按文件顺序更新同一个哈希对象。这里的 1 MiB 是读取缓冲大小,不是 SHA-256 算法内部的块大小,也不代表任何机器上的最优性能。
保存下面的代码为 check_sha256.py:
import argparse
import hashlib
from pathlib import Path
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for block in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("file", type=Path)
parser.add_argument("expected", help="发布者提供的 SHA-256 值")
args = parser.parse_args()
expected = args.expected.strip().lower()
if len(expected) != 64 or any(c not in "0123456789abcdef" for c in expected):
parser.error("expected 必须是 64 位十六进制字符串")
try:
actual = sha256_file(args.file)
except OSError as error:
parser.exit(2, f"无法读取文件:{error}\n")
print(f"实际 SHA-256:{actual}")
matched = actual == expected
print("校验通过" if matched else "校验失败,请检查文件与校验值")
raise SystemExit(0 if matched else 1)
二进制模式 rb 保留文件原始字节,避免文本解码或换行处理影响结果。循环读到空字节串时结束,因此空文件也会得到确定的摘要。所有数据块必须更新同一个 digest;逐块计算哈希后再拼接字符串,并不是整个文件的 SHA-256。
二、运行与自查
在文件下载完毕、没有其他程序继续写入后运行:
python3 check_sha256.py ./package.zip 发布页面给出的64位SHA256值
上面最后一个参数需要替换为真实校验值,不包含文件名。脚本以退出码 0 表示一致、1 表示不一致、2 表示参数或读取错误,便于接入后续自动化。校验失败时,先核对版本、文件名和期望值,再检查下载是否中断,不要直接覆盖期望值来“修复”。
本文核心分块函数已用三类输入验证:空文件、五字节的 hello,以及 2700000 字节的多块数据;计算结果均与同一内容一次性计算的结果一致。测试只验证实现结果,没有进行性能基准测试。
三、相同摘要能说明什么
校验值应该来自可信发布页面或可信校验清单。如果文件和校验值都来自同一个不可信来源,二者一致也不能证明软件安全。摘要比较用于检查内容是否匹配;确认发布者身份还需要可信渠道或签名验证。文件在计算期间发生变化,也会让结果失去明确含义,备份场景应对稳定副本计算摘要。
参考:Python 3.12 官方文档,标准库参考中的 hashlib(安全哈希与消息摘要)章节。
本文由 AI 辅助撰写,示例的核心分块函数已在本地运行验证。
- 点赞
- 收藏
- 关注作者
评论(0)