登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  python教程

Python hashlib 怎样流式计算大文件摘要

来源:17golang原创

时间:2026-10-23 08:48:37 149浏览 收藏

Python hashlib 计算大文件摘要时,可以用二进制模式打开文件,每次读取固定大小的字节块,并依次调用同一个 SHA-256 对象的 update()。不要先 read() 全部内容,也不要把各块各自的摘要拼成最终摘要。Python 3.11 增加的 file_digest() 则提供了专门的文件摘要入口,适合不需要自定义读取逻辑的场景。

从整文件读取改为有界读取

假设一个归档任务需要给导出的压缩包生成摘要,压缩包大小不断增长。原先“一次读取再散列”的函数虽然短,却把文件大小直接变成进程内存压力。这里的目标不是宣称换一个函数就能提速,而是先把应用层读取缓冲限制在一个可解释的范围内。

分块不会改变需要读取的总字节数,也不会把普通 SHA-256 变成分片摘要协议。块大小只是读取策略。例如 1 MiB 可作为起点,但并非通用最快配置;实际速度还受到存储、缓存、CPU 和并发任务数影响,不应把示例参数当成性能结论。

保留一个摘要对象累积原始字节

import hashlib

def sha256_chunks(path, chunk_size=1024 * 1024):
    if not isinstance(chunk_size, int) or isinstance(chunk_size, bool):
        raise TypeError("块大小必须是整数")  # 避免配置把布尔值当数字
    if chunk_size 

这个函数只读取文件,不修改源文件。每次 block 替换上一次引用,不把所有块放进列表,因此应用层缓冲规模与 chunk_size 同阶,而不是与整个文件同阶;对象临时分配和底层 I/O 缓冲仍然存在,不能把进程总占用精确说成只有 1 MiB。

空文件不需要例外分支:没有字节更新时返回 SHA-256 的空输入摘要,不是空字符串。文件打不开、权限不足或读取失败时,让相应异常传给调用方,不要返回空值冒充成功。二进制模式也避免了文本解码和换行转换改变输入内容。

文件句柄、读取缓冲和单一摘要状态之间的静态依赖
静态说明图:文件读取分组提供字节块,摘要对象分组保存累积状态。关系线不是执行时间线,也不是内存测量结果。

在 Python 3.11 及以上使用 file_digest

file_digest 是 Python 3.11 已加入的标准库能力,不是必须等待新版本才可使用的功能。如果已有运行环境满足条件,且只是对普通文件求摘要,可以把手写循环换成专用入口。若仍支持不具备这个入口的环境,保留前面的分块函数即可。

import hashlib

def sha256_file_digest(path):
    with open(path, "rb") as source:  # 传入阻塞、可读的二进制文件
        result = hashlib.file_digest(source, "sha256")
        return result.hexdigest()  # 在本函数内使用结果,文件由 with 关闭

file_digest 返回的是摘要对象,而不是十六进制字符串,因此仍需 hexdigest()。调用后不要假定 source 仍处于可继续读取的已知状态;若后续还要处理文件内容,重新打开一个独立句柄更清楚。普通路径打开的磁盘文件不涉及非阻塞流;不要把这个示例直接套到异步管道或非阻塞网络对象上。Python 3.14 对非阻塞文件的行为作了调整,不能拿旧版本行为当兼容策略。

两种接口的共同点是按文件字节计算同一种算法的摘要;差别在于谁负责读取策略。需要按已读字节报告进度时,手写循环容易放入明确的进度通知;只要一个结果时,专用函数能减少维护代码。这里未执行性能对比,不声称某种写法在所有机器上更快。

迁移时明确文件所有权与错误处理

归档任务接入时,先规定待计算文件何时固定下来。若另一个进程在读取期间继续追加或修改文件,最终摘要对应的是本次读取到的字节,不一定代表某个一致的文件版本。优先对已经完成写入并交付的文件求摘要;需要强一致性时,由上游提供不可变对象或快照,而不是指望散列函数解决并发写入。

不要把“对文件内容求 SHA-256”偷换成“证明文件来自可信发布者”。这里实现的是内容摘要,不是签名校验;也不涉及密码存储。归档清单应同时保留算法名称、文件标识和摘要,避免只存一个没有语义的字符串。读取失败必须进入失败记录,不能登记半截文件的摘要为完整结果。

需求可选写法保留的边界
只生成文件摘要3.11+ 的 file_digest函数拥有并关闭句柄
读取时报告进度分块 read + update进度统计不改变字节顺序
支持较旧环境保留分块函数不引入新接口依赖
文件仍在写入先固定文件版本摘要函数不提供快照保证
摘要入口与文件句柄所有权及错误处理的边界
静态说明图:调用者、摘要函数与资源责任分组区分。关闭句柄和报告读取错误是职责约定,不是一次真实执行记录。

用独立样例理解摘要一致性

读者可以先用极小的内存样例理解“同一个对象分次 update”与“拼接后一次计算”的一致性,再在自己的文件上比较两种文件函数。以下断言仅提供复现材料,本文未运行代码,也没有生成压测数据或文件校验回执。

import hashlib

parts = [b"archive:", b"item-245", b":ready"]
state = hashlib.sha256()
for part in parts:
    state.update(part)  # 保留顺序,向同一个对象累计

expected = hashlib.sha256(b"".join(parts)).hexdigest()
assert state.hexdigest() == expected  # 小样例允许一次拼接,不用于大文件

实际接入前还应覆盖空文件、短于一块、恰好一块、多块带尾部不足一块,以及不存在或不可读的文件。块大小可以换成几个正整数,对同一份稳定文件得到的摘要应一致;如果不一致,应先查是否改变了读取位置、输入字节或文件版本,而不是调整十六进制输出格式。

能先算每一块的摘要,再把摘要相加吗?

不能把那种结果当成普通文件 SHA-256。那是另一套组合规则,需要单独定义协议;本文的 update 接收原始字节块。

文件扩展名会影响摘要吗?

本文只读取内容,路径用于定位文件,扩展名不进入摘要输入。若业务要把名称纳入内容标识,必须另行定义明确的编码和拼接规范。

块越大就一定越快吗?

不是。增大缓冲可能降低调用频率,也会增加同时处理多个文件时的内存需求;选择参数应结合实际工作负载,而不是把整个文件重新装进内存。

官方参考:hashlib 文件摘要与 update 接口;Python 二进制 I/O。示例围绕归档任务独立编写,不包含运行结果。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>