Go 计算SHA256哈希值

45 min read
package main

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"os"
)

type FileUtil struct{}

func (fu *FileUtil) FileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func (fu *FileUtil) CalculateSHA256(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	defer file.Close()

	hasher := sha256.New()
	if _, err := io.Copy(hasher, file); err != nil {
		return "", err
	}

	hashBytes := hasher.Sum(nil)
	return hex.EncodeToString(hashBytes), nil
}

func (fu *FileUtil) CheckAndDownloadFile(filePath, expectedChecksum string, downloadFunc func(string) error) error {
	exists, err := fu.FileExists(filePath)
	if err != nil {
		return err
	}

	if exists {
		checksum, err := fu.CalculateSHA256(filePath)
		if err != nil {
			return err
		}

		if checksum != expectedChecksum {
			fmt.Printf("UserWarning: %s exists, but the SHA256 checksum does not match; re-downloading the file\n", filePath)
			return downloadFunc(filePath)
		}
	} else {
		return downloadFunc(filePath)
	}

	return nil
}

func main() {
	const expectedChecksum = "EXPECTED_CHECKSUM_HERE"
	filePath := "/home/pan/.cache/whisper/large-v2.pt"

	fu := &FileUtil{}
	err := fu.CheckAndDownloadFile(filePath, expectedChecksum, func(path string) error {
		// Add your file download code here
		fmt.Println("Downloading file:", path)
		return nil
	})

	if err != nil {
		panic(err)
	}
}

io.Copy 是 Go 语言中的一个实用函数,用于将数据从 io.Reader 接口实现的对象(如文件)拷贝到 io.Writer 接口实现的对象。在这个例子中,hasher 是一个 sha256 哈希器,实现了 io.Writer 接口,而 file 是一个打开的文件,实现了 io.Reader 接口。

当调用 io.Copy(hasher, file) 时,数据从文件 file 逐步读取,然后逐步写入哈希器 hasher。在这个过程中,哈希器根据写入的数据逐步计算哈希值。

最终,当所有数据都从文件中读取并写入哈希器时,io.Copy 返回,表示数据已成功拷贝。在这之后,您可以使用 hasher.Sum(nil) 获取计算出的哈希值。

此方法的优点是,它不需要一次性将整个文件加载到内存中,而是以流式方式处理数据,这在处理大文件时特别有用。

总结: io.Copy(hasher, file) 从打开的文件 file 读取数据,并将数据写入 hasher,这样哈希器就可以逐步计算文件的 SHA256 哈希值。当所有数据处理完毕,hasher 就包含了文件的完整哈希值。