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

File 如何在Go中一步返回哈希和字节?,file,go,hash,File,Go,Hash,我试图理解如何读取文件内容,计算其哈希值并一次性返回其字节。到目前为止,我分两步来做,例如 // 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.Co

我试图理解如何读取文件内容,计算其哈希值并一次性返回其字节。到目前为止,我分两步来做,例如

// 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)

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

do
data,err:=ioutil.ReadFile(fname)
first。您将获得您的字节片。然后创建您的哈希程序,并执行
哈希程序。写入(数据)

您可以直接将字节写入哈希程序。例如:

package main

import (
    "crypto/sha256"
    "encoding/hex"
    "io/ioutil"
)

func main() {
    hasher := sha256.New()

    data, err := ioutil.ReadFile("foo.txt")
    if err != nil {
        panic(err)
    }

    hasher.Write(data)
    cksum := hex.EncodeToString(hasher.Sum(nil))

    println(cksum)
}

因为散列接口嵌入io.Writer。这允许您从文件中读取一次字节,将它们写入哈希器,然后将它们返回

如果要读取文件,而不在内存中创建整个文件的副本,同时计算其哈希值,则可以使用:

这里发生的事情是,从
数据
读取的任何字节(它是
读取器
,就像文件对象
f
一样)也将被推送到
哈希器

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


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

如果您计划对文件进行哈希处理,则不应将整个文件读取到内存中,因为。。。有一些大文件不适合RAM。是的,在实践中,您很少会遇到这样的内存不足问题,但您可以轻松地防止它们。接口是一个
io.Writer
。通常,散列包有一个返回散列的函数。这允许您以块的形式读取文件,并将其连续馈送到Hash的
Write
方法。您也可以使用诸如
io.Copy
之类的方法来执行此操作:

h := sha256.New()
data := &bytes.Buffer{}
data.Write([]byte("hi there"))
data.Write([]byte("folks"))
io.Copy(h, data)
fmt.Printf("%x", h.Sum(nil))

io.Copy
在内部使用32KiB的bufer,因此使用它需要大约32KiB的最大内存。

即使它可以工作,但会产生不一致的结果,io.Copy返回int64,而io.Writer返回int。当然,我可以稍后再转换到前者,但它让我觉得它是特定于体系结构的,当我需要处理大型文件时,它会导致问题。我在上面发布了io.Copy和io.Write,它们提供了不同的数据类型int64和int,因此结果取决于平台。如何解决这个问题?如果你的文件太大,无法容纳int,那么你就不能让字节片足够大来容纳它们的内容,因为片的长度是int。(如果你在32位平台上运行,这是一个问题,你可能也没有足够的RAM来读取这么大的文件。)我遇到的关于如何使用io.TeeReader的最佳示例。又短又甜。谢谢
h := sha256.New()
data := &bytes.Buffer{}
data.Write([]byte("hi there"))
data.Write([]byte("folks"))
io.Copy(h, data)
fmt.Printf("%x", h.Sum(nil))