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

Python hashlib.file_digest 怎样直接计算文件摘要

来源:17golang原创

时间:2026-11-13 18:29:38 300浏览 收藏

如果目标是从一个已经打开的二进制文件直接得到 SHA-256、SHA-512 等摘要,Python 3.11 及以上可以使用 hashlib.file_digest()。它把“读取文件、持续更新哈希对象、返回最终摘要”这段固定样板收拢成一次调用:

import hashlib

def file_sha256(path):
    # 以二进制、阻塞模式打开文件,file_digest 会消费其中的内容。
    with open(path, "rb") as source:
        digest = hashlib.file_digest(source, "sha256")
    # hexdigest 适合写入清单、日志或与另一份十六进制摘要比较。
    return digest.hexdigest()

官方地址:https://docs.python.org/3/library/hashlib.html#hashlib.file_digest

这篇只讨论文件对象、摘要算法、文件位置和迁移边界,不把文件摘要当成密码存储方案,也不展开大文件分片协议。

file_digest 解决的是哪一段重复代码

旧代码通常先创建哈希对象,再循环读取固定大小的字节块,最后调用 hexdigest()。这种写法没有错,但每个项目都要重复处理块大小、循环结束和资源关闭。file_digest(fileobj, digest) 直接接收一个可读的二进制文件对象,并返回已经更新完成的摘要对象。

第二个参数可以是算法名称字符串、哈希构造器,或返回哈希对象的可调用对象。得到的仍然是标准 hashlib 哈希对象,因此可以继续使用 digest()、hexdigest() 和 name 等接口。它不是把结果直接返回成字符串,格式化动作仍由调用方决定。

Python hashlib.file_digest 从二进制文件对象连接到摘要对象和十六进制结果的静态结构图
图1:file_digest 的静态调用关系;文件对象提供字节内容,算法参数决定摘要对象,调用方再选择二进制或十六进制结果。

用算法名称直接计算 SHA-256

最常见的场景是把算法名作为字符串传入。文件必须用 rb 打开,因为摘要计算针对的是原始字节;如果用文本模式读取,换行转换或编码处理就可能让摘要与原文件不一致。

from pathlib import Path
import hashlib

def digest_file(path: str, algorithm: str = "sha256") -> str:
    # 先保留路径语义,打开动作放在 with 内,确保异常时也能关闭句柄。
    file_path = Path(path)
    with file_path.open("rb") as source:
        digest = hashlib.file_digest(source, algorithm)
    # 返回稳定的十六进制文本,不把摘要对象泄漏到资源管理范围之外。
    return digest.hexdigest()

sha256_text = digest_file("release.tar.gz")
print(sha256_text)

算法名称写错或当前构建不可用时,异常会在调用处暴露。不要为了“兼容”而静默改成另一个算法,否则清单和校验端会得到不同语义。发布文件校验一般会约定算法名与十六进制大小写,保存摘要前先确定这两个约定。

文件位置和关闭责任不能忽略

file_digest 会消费文件对象的内容。官方文档还特别提醒:函数可能绕过 Python 层 I/O,直接使用 fileno(),调用成功或抛出异常后,文件对象都应视为处于未知状态。因此最稳妥的边界是为一次摘要创建一个新打开的文件对象,并让 with 负责关闭。

import hashlib

def digest_from_existing(source) -> str:
    # 调用方若复用文件对象,必须先明确自己要摘要的字节范围。
    source.seek(0)
    digest = hashlib.file_digest(source, "sha256")
    # file_digest 返回后不再依赖 source 的当前位置,调用方仍要负责关闭它。
    return digest.hexdigest()

这个示例表达的是边界,不是说所有文件对象都支持 seek(0)。如果对象来自网络流、管道或某些 socket 包装,它可能只能向前读取;这时应在接口层决定是否允许一次性消费,不能假设摘要函数会自动回退到开头。

文件还应以阻塞模式打开。Python 3.14 起,非阻塞文件会抛出 BlockingIOError;迁移旧代码时如果摘要接口突然在特定文件描述符上失败,先检查打开模式和上游 I/O 封装,而不是重试同一个非阻塞对象。

Python file_digest 的输入模式、文件位置、阻塞要求和调用方关闭责任边界结构图
图2:文件摘要的边界结构;二进制阻塞对象进入 file_digest,文件位置与关闭责任仍属于调用方,非阻塞模式落在异常边界。

需要 HMAC 时传入可调用的摘要工厂

如果任务不是单纯计算公开文件指纹,而是要把文件内容纳入 HMAC,可以传入返回哈希对象的可调用对象。官方示例使用一个已经创建的 HMAC 对象,并通过 lambda 把它交给 file_digest:

import hashlib
import hmac

def file_hmac(path: str, secret: bytes) -> str:
    # HMAC 对象负责密钥语义,file_digest 只负责把文件字节送入它。
    mac = hmac.HMAC(secret, digestmod=hashlib.sha256)
    with open(path, "rb") as source:
        digest = hashlib.file_digest(source, lambda: mac)
    # 返回 HMAC 的十六进制结果;密钥不要写入日志或文章示例的真实配置。
    return digest.hexdigest()

这里的 lambda: mac 返回同一个 HMAC 对象,因此调用完成后 digest 与 mac 指向同一个结果对象。实际业务中应把密钥放在受控的密钥配置中,文件摘要和 HMAC 的用途、密钥管理与校验协议也要分开描述。

从手写循环迁移时检查这五项

检查项迁移前后要保持的语义
输入模式保持二进制读取,不能把文本解码后的字符重新编码来代替原始字节。
算法名称明确使用 sha256、sha512 或约定的构造器,不因异常静默替换算法。
文件位置确认对象从哪里开始读;复用对象时不要把上一次读取留下的位置当成默认起点。
阻塞属性文件对象应处于阻塞模式,非阻塞对象在新版本中可能抛出 BlockingIOError。
资源生命周期file_digest 不替调用方关闭文件;优先用 with 管理 open 返回的对象。

如果只是校验发布包,最小迁移通常就是“保留算法约定、把读取循环替换为 file_digest、继续用 hexdigest 输出”。如果目标是密码存储、抗篡改签名或密钥派生,应该选择对应的密码学接口,不要仅因为摘要 API 方便就把普通哈希直接当作安全方案。

常见问题

file_digest 从 Python 哪个版本开始提供?

官方文档标注它在 Python 3.11 加入。需要支持更老版本时,保留手写的 read() 与 update() 循环,或在项目兼容层中做明确分支。

调用 file_digest 后还能继续读取同一个文件对象吗?

不要依赖它调用后的文件位置或内部状态。文档要求把对象状态视为未知;如果后续还要读取,重新打开文件或在接口边界重新建立明确的读取策略。

为什么一定要用 rb 而不是 r?

摘要针对字节序列。文本模式会引入解码和平台换行语义,可能让参与摘要的内容不再等于磁盘上的原始字节。

file_digest 能代替密码哈希吗?

不能直接这样理解。它是文件或文件类对象的摘要便捷接口;密码存储需要带盐、可调成本和专门的密钥派生方案。

一句话记忆:为一次摘要准备一个二进制、阻塞的文件对象,用明确的算法调用 hashlib.file_digest,结果由调用方格式化,文件仍由调用方关闭。
声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>