Golang中如何从io.Reader计算哈希并追加至末尾?
解决方案
你需要自定义一个实现io.Reader接口的结构体,让它先读取原始数据并计算哈希,等原始数据完全消费后再输出哈希值。这样就能实现哈希的懒生成,只有在原始reader读完后才计算哈希。
实现思路
- 用
io.TeeReader把原始reader和哈希器绑定,确保读取原始数据的同时将数据写入哈希器。 - 自定义reader结构体,维护以下状态:
- 绑定了哈希器的源reader
- 哈希器实例
- 存储生成的哈希字节
- 标记是否已读完原始数据
- 在
Read方法中,优先读取原始数据;当原始数据读完后,立即计算哈希,然后开始输出哈希字节。
代码示例
import ( "io" "crypto/md5" // 如果用标准库MD5,否则替换成你的哈希库 ) // HashAppenderReader 实现io.Reader,先读原始数据,读完后追加哈希 type HashAppenderReader struct { src io.Reader hasher hash.Hash // 若使用自定义哈希库,替换为对应类型 hashBytes []byte done bool } func NewHashAppenderReader(in io.Reader) (*HashAppenderReader, error) { // 初始化哈希器,这里以标准库MD5为例 hasher := md5.New() // 用TeeReader绑定原始reader与哈希器,读数据时自动喂给哈希器 src := io.TeeReader(in, hasher) return &HashAppenderReader{ src: src, hasher: hasher, }, nil } // Read 实现io.Reader接口 func (r *HashAppenderReader) Read(p []byte) (n int, err error) { if !r.done { // 先读取原始数据 n, err = r.src.Read(p) if err == io.EOF { // 原始数据读完,计算哈希 r.hashBytes = r.hasher.Sum(nil) r.done = true // 填充当前p的剩余空间,返回部分哈希数据 if len(r.hashBytes) > 0 { m := copy(p[n:], r.hashBytes) n += m r.hashBytes = r.hashBytes[m:] err = nil // 还有哈希数据未返回,暂不返回EOF } } return n, err } // 原始数据已读完,返回剩余哈希字节 if len(r.hashBytes) == 0 { return 0, io.EOF } n = copy(p, r.hashBytes) r.hashBytes = r.hashBytes[n:] return n, nil }
使用方式
// 假设in是你的原始文件reader newInput, err := NewHashAppenderReader(in) if err != nil { // 处理错误 } // 读取newInput时,会先读完in的所有数据,再自动追加哈希值 _, err = io.Copy(outputWriter, newInput)
适配你的自定义哈希库
如果要替换成你代码中的hash.NewMultiHasherTypes,只需调整哈希器初始化和哈希计算逻辑:
import ( "io" "encoding/hex" // 导入你的哈希库 ) type HashAppenderReader struct { src io.Reader hasher hash.MultiHasherTypes // 匹配你的哈希器类型 hashBytes []byte done bool } func NewHashAppenderReader(in io.Reader) (*HashAppenderReader, error) { preHasher, err := hash.NewMultiHasherTypes(hash.NewHashSet(hash.MD5)) if err != nil { return nil, err } src := io.TeeReader(in, preHasher) return &HashAppenderReader{ src: src, hasher: preHasher, }, nil } // 修改Read方法中的哈希计算部分: if err == io.EOF { hashStr := r.hasher.Sums()[hash.MD5] byteHash, err := hex.DecodeString(hashStr) if err != nil { return 0, err } r.hashBytes = byteHash r.done = true // 后续填充逻辑不变 }
这样就能保证哈希值是在原始reader完全消费后才生成的,完美解决你之前的问题。
内容的提问来源于stack exchange,提问作者Tom Raganowicz
相关产品推荐
相关产品推荐

