用 Python hashlib 校验下载文件:分块读取与 SHA-256 的边界

举报
yd_217337598 发表于 2026/10/23 10:08:43 2026/10/23
【摘要】 用 Python 标准库分块计算文件 SHA-256,核对可信来源的摘要,并用退出码区分一致、不一致和输入错误。附可运行代码及已验证的小样本,说明文件版本、二进制读取与校验边界。

下载压缩包或安装文件后,可以先核对发布方提供的 SHA-256 摘要,再决定是否使用。本文写一个只读命令行工具:按块读取文件,输出实际摘要,并通过退出码区分一致、不一致和输入错误。摘要必须来自你信任的发布渠道;把自己刚算出的值再拿来对比自己,并不能验证下载是否正确。

先明确比较对象

程序比较的是文件字节,而不是文件名、扩展名或解压后的内容。同一内容改名后摘要不变;文本多一个换行,或压缩包重新打包,摘要都可能改变。因此应核对同一版本、同一平台和同一格式的文件,不能拿 Windows 安装包的摘要去验证 Linux 安装包。

可运行代码

将下面内容保存为 check_sha256.py,只需 Python 标准库。以二进制模式 rb 打开文件,避免文本解码和换行转换改变参与计算的数据。每次最多读取 1 MiB,并把数据依次交给同一个摘要对象;这里选择的是读取缓冲区大小,并非 SHA-256 算法的内部块大小。

import argparse
import hashlib
import re
from pathlib import Path


def sha256_file(path, chunk_size=1024 * 1024):
    if chunk_size <= 0:
        raise ValueError("chunk_size 必须为正数")
    digest = hashlib.sha256()
    with Path(path).open("rb") as stream:
        for chunk in iter(lambda: stream.read(chunk_size), b""):
            digest.update(chunk)
    return digest.hexdigest()


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="核对文件的 SHA-256 摘要")
    parser.add_argument("file", type=Path)
    parser.add_argument("expected", help="可信来源提供的 64 位十六进制摘要")
    args = parser.parse_args()
    if not re.fullmatch(r"[0-9a-fA-F]{64}", args.expected):
        parser.error("expected 必须是 64 位十六进制字符串")
    try:
        actual = sha256_file(args.file)
    except OSError as error:
        parser.exit(2, f"读取失败:{error}\n")
    print(actual)
    matched = actual == args.expected.lower()
    print("校验一致" if matched else "校验不一致")
    raise SystemExit(0 if matched else 1)

先用一个可重复的小样本

下面的命令创建恰好包含三个字节 abc 的文件,不带换行。再用给定摘要运行检查,应该打印“校验一致”,退出码为 0。路径含空格时,运行命令应给路径加引号。

python3 -c "from pathlib import Path; Path('sample.bin').write_bytes(b'abc')"
python3 check_sha256.py sample.bin ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

把文件内容改为 abd,保留原来的期望摘要再运行,应提示“校验不一致”并返回 1。摘要字符串可以使用大写十六进制,代码会统一转成小写。长度不足、包含非十六进制字符、文件不存在或路径实际是目录时,程序返回 2;这些情况应当修正输入,不能当作校验通过。

分块读取解决什么问题

直接 read_bytes() 会一次读入整个文件。本例每读一块便更新摘要,避免为文件正文分配同等大小的缓冲区;文件越大,分块的价值越明显。不过程序仍然要读取全部内容,无法靠较小缓冲区跳过计算。空文件也有合法摘要,不应因为读取的第一块为空就把它当成异常。

核对结果的使用边界

摘要一致说明本次读到的数据与期望摘要相符,不等于软件已经经过安全审计,也不证明发布者身份。若文件和摘要都来自同一个被替换的页面,对比可能无法发现问题;需要结合可信发布渠道,必要时再验证数字签名。读取期间也应避免其他程序继续修改文件,本工具没有加锁,不能提供文件系统事务快照。

本文示例已在临时目录验证空文件、abc 样本、多块二进制内容、大写摘要、内容变化,以及缺失路径、目录和非法摘要的退出码;还验证了函数拒绝零缓冲区。实际校验时先确认版本与来源,再处理不一致的原因,避免仅因文件名相同就跳过检查。

参考:Python 官方 hashlib 文档:https://docs.python.org/zh-cn/3/library/hashlib.html 。本文说明、工具和验证样例为本次独立编写。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。