golang下载文件并计算md5

702 阅读1分钟

golang下载文件并计算md5

​ 今天遇到一个需求,需要从远程服务器下载文件,并对文件做一些其他处理(eg:计算MD5,计算sha256,计算sha1,并生成种子文件......)

​ 中间遇到个文件,request.body 一旦被 md5.Write,sha256 再做 write 操作就无法从request.body中读取出来数据了。

	......
	resp, _ := client.Do(request)
	var md5Hash := md5.New()
    var shaHash := sha256.New()
	md5Hash.Write(resp.Body)
	shaHash.Write(resp.Body)

​ 这种操作 shaHash.Write 就无法写入数据,生成的是0 byte的 sha256 的值。

​ 解决这个问题,需要写一个结构体,实现 io.Reader 接口。然后在read的时候做处理。示例代码如下

type IoReader struct {
	Reader    io.ReadCloser
	ReadBytes uint64    // 已经读取的字节数
	LogBytes  uint64    // 每次打日志的字节数
	Md5       hash.Hash // md5生成
    Sha256    hash.Hash // 生成sha256
}

func NewIoBodyReader(rc io.ReadCloser) *IoReader {
	return &IoReader{
		Reader: rc,
		Md5:    md5.New(),
        Sha256:	sha256.New(),
	}
}
func (s *IoReader) Read(p []byte) (n int, err error) {
	count, err := s.Reader.Read(p)
	s.Md5.Write(p[:count])
    s.Sha256.Write(p[:count])

	s.ReadBytes += uint64(count)
	s.LogBytes += uint64(count)
	if s.LogBytes >= 1024 {
		log.Printf("已经读取 %d 字节", s.ReadBytes)
		s.LogBytes = 0
	}
	return count, err
}

使用上面的结构去写文件,eg:

	file, _ := os.OpenFile("tmp.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	defer file.Close()
	buf := make([]byte, 10240)
	writer := bufio.NewWriter(file)
	if _, err = io.CopyBuffer(writer, ioReader, buf); err != nil {
		log.Printf("copy errr:%s", err.Error())
		return
	}
	fmt.Printf("md5:%x\n", ioReader.Md5.Sum(nil))
	writer.Flush()

完整示例:

func main(){
	var (
		url  = "http://www.baidu.com"
		data = map[string]interface{}{
			"name": "light",
		}
		reader io.Reader
	)
	dataByte, _ := json.Marshal(data)
	reader = bytes.NewReader(dataByte)
	request, err := http.NewRequest("POST", url, reader)
	request.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36")

	client := &http.Client{}
	client.Timeout = CallTimeOut
	resp, err := client.Do(request)
	if err != nil {
		return
	}

	if request.Body != nil {
		defer request.Body.Close()
	}
	defer resp.Body.Close()

	ioReader := NewIoBodyReader(resp.Body)

	file, _ := os.OpenFile("tmp.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	defer file.Close()
	buf := make([]byte, 10240)
	writer := bufio.NewWriter(file)
	if _, err = io.CopyBuffer(writer, ioReader, buf); err != nil {
		log.Printf("copy errr:%s", err.Error())
		return
	}
	fmt.Printf("md5:%x\n", ioReader.Md5.Sum(nil))
    fmt.Printf("md5:%x\n", ioReader.Sha256.Sum(nil))
	writer.Flush()
}

可以看到打印出来的md5,和sha256,跟文件的一致。