HashingContext
2026/4/14大约 4 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — HashingContext
HashingContext
定义
HashingContext 是一个可以"分块"计算哈希值的工具类。普通的 md5_text() 或 sha256_text() 需要一次性把所有数据都传进去,但 HashingContext 允许你一块一块地喂数据,最后汇总得到最终的哈希值。
打个比方:普通哈希就像一次称完所有粮食的重量,而 HashingContext 就像一个漏斗——你可以一小袋一小袋地往里倒粮食,最后得到总重量。这对于处理大文件特别有用,因为你不需要一次性把整个文件读进内存。
在实际游戏开发中,当你需要计算大文件(比如几百 MB 的资源包)的哈希值时,如果一次性读取整个文件会占用大量内存。使用 HashingContext 可以分块读取、分块计算,内存占用始终很小。
函数签名
C#
// C# 中通过 System.Security.Cryptography 的 IncrementalHash 实现类似功能
using System.Security.Cryptography;
var hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
hasher.AppendData(dataChunk1);
hasher.AppendData(dataChunk2);
byte[] hash = hasher.GetHashAndReset();GDScript
var ctx = HashingContext.new()
ctx.start(HashingContext.HASH_SHA256) # 开始哈希计算
ctx.update(chunk1) # 更新:喂入第一块数据
ctx.update(chunk2) # 更新:喂入第二块数据
var result = ctx.finish() # 完成:返回最终的哈希值(PackedByteArray)参数说明
| 方法 | 参数 | 说明 |
|---|---|---|
start(hashType) | HASH_MD5 或 HASH_SHA256 | 开始哈希计算,选择哈希算法 |
update(data) | PackedByteArray | 喂入一块数据 |
finish() | 无 | 结束计算,返回最终的哈希值 |
返回值
finish() 返回 PackedByteArray(原始字节数组)。可以用 .hex_encode() 转为十六进制字符串,或用 String.HexEncode() 方法。
代码示例
基础用法:分块计算 SHA-256
C#
using System.Security.Cryptography;
using System.Text;
var hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
hasher.AppendData(Encoding.UTF8.GetBytes("Hello "));
hasher.AppendData(Encoding.UTF8.GetBytes("World"));
byte[] hash = hasher.GetHashAndReset();
string hex = Convert.ToHexString(hash).ToLower();
GD.Print(hex);
// 运行结果: "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"
// 等同于 "Hello World".sha256_text() 的结果GDScript
var ctx = HashingContext.new()
ctx.start(HashingContext.HASH_SHA256)
ctx.update("Hello ".to_utf8_buffer())
ctx.update("World".to_utf8_buffer())
var result = ctx.finish()
var hex = result.hex_encode()
print(hex)
# 运行结果: "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"
# 等同于 "Hello World".sha256_text() 的结果实际场景:分块计算大文件的哈希值
C#
using Godot;
using System.Security.Cryptography;
public partial class FileHasher : Node
{
public string HashLargeFile(string filePath, int chunkSize = 65536)
{
var file = FileAccess.Open(filePath, FileAccess.ModeFlags.Read);
if (file == null)
{
GD.PrintErr($"无法打开文件:{filePath}");
return "";
}
using var hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
long totalSize = file.GetLength();
long bytesRead = 0;
while (!file.EofReached())
{
byte[] chunk = file.GetBuffer(chunkSize);
if (chunk.Length == 0) break;
hasher.AppendData(chunk);
bytesRead += chunk.Length;
}
file.Close();
byte[] hash = hasher.GetHashAndReset();
string hex = Convert.ToHexString(hash).ToLower();
GD.Print($"文件哈希计算完成,读取 {bytesRead} 字节");
// 运行结果: 返回大文件的 SHA-256 哈希值
return hex;
}
}GDScript
extends Node
func hash_large_file(file_path: String, chunk_size: int = 65536) -> String:
var file = FileAccess.open(file_path, FileAccess.READ)
if file == null:
push_error("无法打开文件:%s" % file_path)
return ""
var ctx = HashingContext.new()
ctx.start(HashingContext.HASH_SHA256)
var total_size = file.get_length()
var bytes_read = 0
while not file.eof_reached():
var chunk = file.get_buffer(chunk_size)
if chunk.size() == 0:
break
ctx.update(chunk)
bytes_read += chunk.size()
file.close()
var result = ctx.finish()
var hex = result.hex_encode()
print("文件哈希计算完成,读取 %d 字节" % bytes_read)
# 运行结果: 返回大文件的 SHA-256 哈希值
return hex进阶用法:计算网络数据流的哈希
C#
using Godot;
using System.Security.Cryptography;
using System.Text;
public partial class StreamHasher : Node
{
private IncrementalHash _hasher;
private long _totalBytes = 0;
public void BeginStream()
{
_hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
_totalBytes = 0;
GD.Print("开始接收数据流...");
// 运行结果: 初始化流式哈希计算
}
public void FeedData(byte[] chunk)
{
_hasher.AppendData(chunk);
_totalBytes += chunk.Length;
GD.Print($"已接收 {_totalBytes} 字节");
// 运行结果: 每次收到数据都更新哈希
}
public string EndStream()
{
byte[] hash = _hasher.GetHashAndReset();
string hex = Convert.ToHexString(hash).ToLower();
GD.Print($"数据流接收完成,共 {_totalBytes} 字节,哈希:{hex[..16]}...");
// 运行结果: 返回整个数据流的 SHA-256 哈希
return hex;
}
}GDScript
extends Node
var _ctx: HashingContext
var _total_bytes: int = 0
func begin_stream():
_ctx = HashingContext.new()
_ctx.start(HashingContext.HASH_SHA256)
_total_bytes = 0
print("开始接收数据流...")
# 运行结果: 初始化流式哈希计算
func feed_data(chunk: PackedByteArray):
_ctx.update(chunk)
_total_bytes += chunk.size()
print("已接收 %d 字节" % _total_bytes)
# 运行结果: 每次收到数据都更新哈希
func end_stream() -> String:
var result = _ctx.finish()
var hex = result.hex_encode()
print("数据流接收完成,共 %d 字节,哈希:%s..." % [_total_bytes, hex.substr(0, 16)])
# 运行结果: 返回整个数据流的 SHA-256 哈希
return hex注意事项
- 必须按顺序调用:
start()->update()(多次) ->finish(),不能跳过或打乱顺序。 finish()之后不能再update():调用finish()后哈希计算结束。如果需要重新计算,需要再调用start()。- C# 使用
IncrementalHash:C# 没有 Godot 的HashingContext,但System.Security.Cryptography.IncrementalHash提供了完全相同的功能。 - 内存友好:分块处理的优点是不需要把整个文件加载到内存中,适合处理大文件。
- 支持 MD5 和 SHA-256 两种算法:
HashingContext.HASH_MD5和HashingContext.HASH_SHA256分别对应两种哈希算法。
