如何在 Go 中一步返回哈希和字节?

Val*_*tin 4 hash file go

我试图了解如何读取文件的内容,计算其散列并一次性返回其字节。到目前为止,我分两步完成,例如

// calculate file checksum
hasher := sha256.New()
f, err := os.Open(fname)
if err != nil {
    msg := fmt.Sprintf("Unable to open file %s, %v", fname, err)
    panic(msg)
}
defer f.Close()
b, err := io.Copy(hasher, f)
if err != nil {
    panic(err)
}
cksum := hex.EncodeToString(hasher.Sum(nil))

// read again (!!!) to get data as bytes array
data, err := ioutil.ReadFile(fname)
Run Code Online (Sandbox Code Playgroud)

显然,这不是最有效的方法,因为读取发生两次,一次在复制中传递给散列器,另一个在 ioutil 中读取文件并返回字节列表。我正在努力理解如何将这些步骤组合在一起并一次性完成,读取数据一次,计算任何散列并将其与字节列表一起返回到另一层。

Jos*_*ahn 9

如果你想读取一个文件,而不是在内存中创建整个文件的副本,同时计算它的哈希值,你可以使用TeeReader:

hasher := sha256.New()
f, err := os.Open(fname)
data := io.TeeReader(f, hasher)
// Now read from data as usual, which is still a stream.
Run Code Online (Sandbox Code Playgroud)

这里发生的事情是从中读取的任何字节data(Reader就像文件对象一样f)也将被推送到hasher。

但是请注意,hasher只有在您通过 读取整个文件后才会产生正确的散列data,直到那时才会产生。因此,如果您在决定是否要读取文件之前需要哈希值,您可以选择分两次执行(例如像现在这样),或者始终读取文件但丢弃如果哈希检查失败,则结果。

如果您确实分两次读取文件,您当然可以在内存中的字节缓冲区中缓冲整个文件数据。但是,操作系统通常会在 RAM 中缓存您刚刚读取的文件(如果可能的话),因此您自己执行缓冲的两遍解决方案而不是仅对文件执行两次传递所带来的性能优势可能可以忽略不计。