简体   繁体   English

有没有办法从 io.Copy 获得传输速度?

[英]Is there a way to get transfer speed from io.Copy?

I am copying a network stream to a file using io.Copy.我正在使用 io.Copy 将网络流复制到文件中。 I would like to extract the current speed, preferably in bytes per second, that the transfer is operating at.我想提取传输运行的当前速度,最好以每秒字节数为单位。

res, err := http.Get(url)

if err != nil {
    panic(err)
}

// Open output file
out, err := os.OpenFile("output", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
    panic(err)
}

// Close output file as well as body    

defer out.Close()

defer func(Body io.ReadCloser) {
    err := Body.Close()
    if err != nil {
        panic(err)
    }
}(res.Body)

_, err := io.Copy(out, res.Body) 

As noted in the comments - the entire transfer rate is easily computed after the fact - especially when using io.Copy .如评论中所述 - 事后很容易计算整个传输速率 - 特别是在使用io.Copy时。 If you want to track "live" transfer rates - and poll the results over a long file transfer - then a little more work is involved.如果您想跟踪“实时”传输速率 - 并在长时间文件传输中轮询结果 - 那么需要做更多的工作。

Below I've outlined a simple io.Reader wrapper to track the overall transfer rate.下面我概述了一个简单的io.Reader包装器来跟踪整体传输速率。 For brevity, it is not goroutine safe, but would be trivial do make it so.为简洁起见,它不是 goroutine 安全的,但这样做是微不足道的。 And then one could poll from another goroutine the progress, while the main goroutine does the reading.然后可以从另一个 goroutine 轮询进度,而主 goroutine 进行读取。


You can create a io.Reader wrapper - and use that to track the moment of first read - and then track future read byte counts.您可以创建一个io.Reader包装器 - 并使用它来跟踪第一次读取的时刻 - 然后跟踪未来的读取字节数。 The final result may look like this:最终结果可能如下所示:

r := NewRater(resp.Body) // io.Reader wrapper

n, err := io.Copy(out, r)

log.Print(r) // stringer method shows human readable "b/s" output

To implement this, one approach:为了实现这一点,一种方法:

type rate struct {
    r          io.Reader
    count      int64 // may have large (2GB+) files - so don't use int
    start, end time.Time
}

func NewRater(r io.Reader) *rate { return &rate{r: r} }

then we need the wrapper Read to track the underlying io.Readers progress:那么我们需要包装器Read来跟踪底层的io.Readers进度:

func (r *rate) Read(b []byte) (n int, err error) {

    if r.start.IsZero() {
        r.start = time.Now()
    }

    n, err = r.r.Read(b) // underlying io.Reader read

    r.count += int64(n)

    if err == io.EOF {
        r.end = time.Now()
    }

    return
}

the rate at any time can be polled like so - even before EOF :任何时候的利率都可以像这样轮询——甚至在EOF之前:

func (r *rate) Rate() (n int64, d time.Duration) {
    end := r.rend
    if end.IsZero() {
        end = time.Now()
    }
    return r.count, end.Sub(r.start)
}

and a simple Stringer method to show b/s :和一个简单的Stringer方法来显示b/s

func (r *rate) String() string {
    n, d := r.Rate()
    return fmt.Sprintf("%.0f b/s", float64(n)/(d.Seconds()))
}

Note: the above io.Reader wrapper has no locking in place, so operations must be from the same goroutine.注意:上面的io.Reader包装器没有锁定,所以操作必须来自同一个 goroutine。 Since the question relates to io.Copy - then this is a safe assumption to make.由于问题与io.Copy - 那么这是一个安全的假设。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM