今天处理GO下载的时候遇到一个问题:如果调用readall去读取response,会造成很大的内存消耗,并且无法获取下载的进度。

直到搜到ioutil.ReadAll(httpResponse.Body) memory consumption,才解决了我的问题。

package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	url := "http://dl.360safe.com/setup.exe"

	resp, _ := http.Get(url)

	filename := "setup.exe"

	f, _ := os.OpenFile(filename, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)

	n, err := copyBuffer(f, resp.Body, nil)

	if err != nil {
		fmt.Println(err, n)
	}
}

func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) {
	// If the reader has a WriteTo method, use it to do the copy.
	// Avoids an allocation and a copy.
	if wt, ok := src.(io.WriterTo); ok {
		return wt.WriteTo(dst)
	}
	// Similarly, if the writer has a ReadFrom method, use it to do the copy.
	if rt, ok := dst.(io.ReaderFrom); ok {
		return rt.ReadFrom(src)
	}
	if buf == nil {
		buf = make([]byte, 1024*1024)
	}
	for {
		nr, er := src.Read(buf)
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = io.ErrShortWrite
				break
			}
		}
		if er == io.EOF {
			break
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

其中copyBuffer的written就可以解决下载进度的问题,并且使用了io.copy打通了读和写的通道,很优雅的解决了问题。