zoukankan      html  css  js  c++  java
  • Go 语言显示下载文件进度/分段读取 io.TeeReader()

    想用go下载文件,代码:

    package main
    
    import (
        "fmt"
        "io"
        "net/http"
        "os"
        "strings"
    )
    
    // WriteCounter counts the number of bytes written to it. It implements to the io.Writer
    // interface and we can pass this into io.TeeReader() which will report progress on each
    // write cycle.
    type WriteCounter struct {
        Total uint64
    }
    
    func (wc *WriteCounter) Write(p []byte) (int, error) {
        n := len(p)
        wc.Total += uint64(n)
        wc.PrintProgress()
        return n, nil
    }
    
    func (wc WriteCounter) PrintProgress() {
        // Clear the line by using a character return to go back to the start and remove
        // the remaining characters by filling it with spaces
        fmt.Printf("
    %s", strings.Repeat(" ", 35))
    
        // Return again and print current status of download
        // We use the humanize package to print the bytes in a meaningful way (e.g. 10 MB)
        fmt.Printf("
    Downloading... %d B complete", wc.Total)
    }
    
    func main() {
        fmt.Println("Download Started")
    
        fileUrl := "https://dl.google.com/go/go1.11.1.src.tar.gz"
        err := DownloadFile("go1.11.1.src.tar.gz", fileUrl)
        if err != nil {
            panic(err)
        }
    
        fmt.Println("Download Finished")
    }
    
    // DownloadFile will download a url to a local file. It's efficient because it will
    // write as it downloads and not load the whole file into memory. We pass an io.TeeReader
    // into Copy() to report progress on the download.
    func DownloadFile(filepath string, url string) error {
    
        // Create the file, but give it a tmp file extension, this means we won't overwrite a
        // file until it's downloaded, but we'll remove the tmp extension once downloaded.
        out, err := os.Create(filepath + ".tmp")
        if err != nil {
            return err
        }
        defer out.Close()
    
        // Get the data
        resp, err := http.Get(url)
        if err != nil {
            return err
        }
        defer resp.Body.Close()
    
        // Create our progress reporter and pass it to be used alongside our writer
        counter := &WriteCounter{}
        _, err = io.Copy(out, io.TeeReader(resp.Body, counter))
        if err != nil {
            return err
        }
    
        // The progress use the same line so print a new line once it's finished downloading
        fmt.Print("
    ")
        out.Close()   //需增加这一句,否则在我的电脑上未成功改名。
        err = os.Rename(filepath+".tmp", filepath)
        if err != nil {
            return err
        }
    
        return nil
    }

    补充:原代码有bug,没能成功改名文件。需要在os.Rename()之前加上out.Close()才行(已补充到上述代码中)

    参考:https://blog.csdn.net/zhizhengguan/article/details/89066645

    https://www.twle.cn/t/385

  • 相关阅读:
    <Android 基础(二十六)> 渐变色圆角Button
    Java 中父类怎么调用子类的方法?
    <Android 基础(二十五)> Frame Animation
    Java基础知识点
    HTTP请求流程(二)----Telnet模拟HTTP请求
    telnet远程登录协议
    HTTP协议详解
    STM32 SPI接口的NSS引脚
    TCP/IP
    以太网帧格式、IP数据报格式、TCP段格式+UDP段格式 详解
  • 原文地址:https://www.cnblogs.com/pu369/p/12932399.html
Copyright © 2011-2022 走看看