1. 带进度条大文件下载

1.1.1. 普通文件下载

本示例说明如何从网上将文件下载到本地计算机。通过io.Copy()直接使用并传递响应主体,我们将数据流式传输到文件中,而不必将其全部加载到内存中-小文件不是问题,但下载大文件时会有所不同。

  1. package main
  2. import (
  3. "io"
  4. "net/http"
  5. "os"
  6. )
  7. func main() {
  8. fileUrl := "http://topgoer.com/static/2/9.png"
  9. if err := DownloadFile("9.png", fileUrl); err != nil {
  10. panic(err)
  11. }
  12. }
  13. // download file会将url下载到本地文件,它会在下载时写入,而不是将整个文件加载到内存中。
  14. func DownloadFile(filepath string, url string) error {
  15. // Get the data
  16. resp, err := http.Get(url)
  17. if err != nil {
  18. return err
  19. }
  20. defer resp.Body.Close()
  21. // Create the file
  22. out, err := os.Create(filepath)
  23. if err != nil {
  24. return err
  25. }
  26. defer out.Close()
  27. // Write the body to file
  28. _, err = io.Copy(out, resp.Body)
  29. return err
  30. }

1.1.2. 带进度条的大文件下载

下面的示例是带有进度条的大文件下载,我们将响应主体传递到其中,io.Copy()但是如果使用a,TeeReader则可以传递计数器来跟踪进度。在下载时,我们还将文件另存为临时文件,因此在完全下载文件之前,我们不会覆盖有效文件。

  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "net/http"
  6. "os"
  7. "strings"
  8. "github.com/dustin/go-humanize"
  9. )
  10. type WriteCounter struct {
  11. Total uint64
  12. }
  13. func (wc *WriteCounter) Write(p []byte) (int, error) {
  14. n := len(p)
  15. wc.Total += uint64(n)
  16. wc.PrintProgress()
  17. return n, nil
  18. }
  19. func (wc WriteCounter) PrintProgress() {
  20. fmt.Printf("\r%s", strings.Repeat(" ", 35))
  21. fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total))
  22. }
  23. func main() {
  24. fmt.Println("Download Started")
  25. fileUrl := "http://topgoer.com/static/2/9.png"
  26. err := DownloadFile("9.png", fileUrl)
  27. if err != nil {
  28. panic(err)
  29. }
  30. fmt.Println("Download Finished")
  31. }
  32. func DownloadFile(filepath string, url string) error {
  33. out, err := os.Create(filepath + ".tmp")
  34. if err != nil {
  35. return err
  36. }
  37. resp, err := http.Get(url)
  38. if err != nil {
  39. out.Close()
  40. return err
  41. }
  42. defer resp.Body.Close()
  43. counter := &WriteCounter{}
  44. if _, err = io.Copy(out, io.TeeReader(resp.Body, counter)); err != nil {
  45. out.Close()
  46. return err
  47. }
  48. fmt.Print("\n")
  49. out.Close()
  50. if err = os.Rename(filepath+".tmp", filepath); err != nil {
  51. return err
  52. }
  53. return nil
  54. }