How to print the bytes while the file is being downloaded ? -golang

huangapple go评论103阅读模式
英文:

How to print the bytes while the file is being downloaded ? -golang

问题

我想知道在文件下载过程中是否可以计算和打印下载的字节数。

out, err := os.Create("file.txt")
defer out.Close()
if err != nil {
    fmt.Println(fmt.Sprint(err))
    panic(err)
}
resp, err := http.Get("http://example.com/zip")
defer resp.Body.Close()
if err != nil {
    fmt.Println(fmt.Sprint(err))
    panic(err)
}

n, er := io.Copy(out, resp.Body)
if er != nil {
    fmt.Println(fmt.Sprint(err))
}
fmt.Println(n, "字节")

以上是代码部分的翻译。

英文:

I'm wondering if it's possible to count and print the number of bytes downloaded while the file is being downloaded.

out, err := os.Create("file.txt")
defer out.Close()
if err != nil {
	fmt.Println(fmt.Sprint(err))
	panic(err)
}
resp, err := http.Get("http://example.com/zip")
defer resp.Body.Close()
if err != nil {
	fmt.Println(fmt.Sprint(err))
	panic(err)
}

n, er := io.Copy(out, resp.Body)
if er != nil {
	fmt.Println(fmt.Sprint(err))
}
fmt.Println(n, "bytes ")

答案1

得分: 39

如果我理解正确,您希望在数据传输过程中显示读取的字节数,可能是为了维护某种进度条或其他内容。在这种情况下,您可以使用Go的组合数据结构来包装自定义的io.Readerio.Writer实现。

它简单地将相应的ReadWrite调用转发到底层流,同时对它们返回的(int, error)值进行一些额外的处理。以下是一个您可以在Go playground上运行的示例:

package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"
)

// PassThru包装现有的io.Reader。
//
// 它简单地转发Read()调用,同时显示从各个调用中返回的结果。
type PassThru struct {
	io.Reader
	total int64 // 总传输字节数
}

// Read '覆盖'底层io.Reader的Read方法。
// 这是io.Copy()将调用的方法。我们只是用它来跟踪字节计数,然后转发调用。
func (pt *PassThru) Read(p []byte) (int, error) {
	n, err := pt.Reader.Read(p)
	pt.total += int64(n)

	if err == nil {
		fmt.Println("Read", n, "bytes for a total of", pt.total)
	}

	return n, err
}

func main() {
	var src io.Reader    // 源文件/URL等
	var dst bytes.Buffer // 目标文件/缓冲区等

	// 创建一些随机输入数据。
	src = bytes.NewBufferString(strings.Repeat("Some random input data", 1000))

	// 使用我们自定义的io.Reader进行包装。
	src = &PassThru{Reader: src}

	count, err := io.Copy(&dst, src)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	fmt.Println("Transferred", count, "bytes")
}

它生成的输出如下:

Read 512 bytes for a total of 512
Read 1024 bytes for a total of 1536
Read 2048 bytes for a total of 3584
Read 4096 bytes for a total of 7680
Read 8192 bytes for a total of 15872
Read 6128 bytes for a total of 22000
Transferred 22000 bytes
英文:

If I understand you correctly, you wish to display the number of bytes read, while the data is transferring. Presumably to maintain some kind of a progress bar or something. In which case, you can use Go's compositional data structures to wrap the reader or writer in a custom io.Reader or io.Writer implementation.

It simply forwards the respective Read or Write call to the underlying stream, while doing some additional work with the (int, error) values returned by them. Here is an example you can run on the Go playground.

package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"
)

// PassThru wraps an existing io.Reader.
//
// It simply forwards the Read() call, while displaying
// the results from individual calls to it.
type PassThru struct {
	io.Reader
	total int64 // Total # of bytes transferred
}

// Read 'overrides' the underlying io.Reader's Read method.
// This is the one that will be called by io.Copy(). We simply
// use it to keep track of byte counts and then forward the call.
func (pt *PassThru) Read(p []byte) (int, error) {
	n, err := pt.Reader.Read(p)
	pt.total += int64(n)

	if err == nil {
		fmt.Println("Read", n, "bytes for a total of", pt.total)
	}

	return n, err
}

func main() {
	var src io.Reader    // Source file/url/etc
	var dst bytes.Buffer // Destination file/buffer/etc

	// Create some random input data.
	src = bytes.NewBufferString(strings.Repeat("Some random input data", 1000))

	// Wrap it with our custom io.Reader.
	src = &PassThru{Reader: src}

	count, err := io.Copy(&dst, src)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	fmt.Println("Transferred", count, "bytes")
}

The output it generates is this:

Read 512 bytes for a total of 512
Read 1024 bytes for a total of 1536
Read 2048 bytes for a total of 3584
Read 4096 bytes for a total of 7680
Read 8192 bytes for a total of 15872
Read 6128 bytes for a total of 22000
Transferred 22000 bytes

答案2

得分: 21

标准库现在提供了类似于jimt的PassThru的功能:io.TeeReader。它可以简化一些事情:

// WriteCounter用于计算写入其中的字节数。
type WriteCounter struct {
    Total int64 // 总的传输字节数
}

// Write实现了io.Writer接口。
//
// 总是完成操作,不会返回错误。
func (wc *WriteCounter) Write(p []byte) (int, error) {
    n := len(p)
    wc.Total += int64(n)
    fmt.Printf("读取了%d字节,总共%d字节\n", n, wc.Total)
    return n, nil
}

func main() {

    // ...

    // 使用我们自定义的io.Reader进行包装。
    src = io.TeeReader(src, &WriteCounter{})

    // ...
}

playground

英文:

The stdlib now provides something like jimt's PassThru: io.TeeReader. It helps simplify things a bit:

// WriteCounter counts the number of bytes written to it.
type WriteCounter struct {
	Total int64 // Total # of bytes transferred
}

// Write implements the io.Writer interface.  
// 
// Always completes and never returns an error.
func (wc *WriteCounter) Write(p []byte) (int, error) {
	n := len(p)
	wc.Total += int64(n)
	fmt.Printf("Read %d bytes for a total of %d\n", n, wc.Total)
	return n, nil
}

func main() {

    // ...    

    // Wrap it with our custom io.Reader.
	src = io.TeeReader(src, &WriteCounter{})

    // ...
}

playground

答案3

得分: 1

grab Go包实现了文件下载的进度更新(以及许多其他功能)。

在下面的教程中,包含了一个在下载过程中打印进度更新的示例:http://cavaliercoder.com/blog/downloading-large-files-in-go.html

你可以简单地调用grab.GetAsync,它会在一个新的Go协程中进行下载,然后监视从调用线程返回的grab.ResponseBytesTransferredProgress

英文:

The grab Go package implements progress updates (and many other features) for file downloads.

An example of printing progress updates while a download is in process is included in the following walkthrough: http://cavaliercoder.com/blog/downloading-large-files-in-go.html

You can basically call grab.GetAsync which downloads in a new Go routine and then monitor the BytesTransferred or Progress of the returned grab.Response from the calling thread.

答案4

得分: 1

其他答案已经解释了PassThru。这里提供一个完整的示例,基于Dave Jack的答案。

package main

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

// writeCounter用于计算写入的字节数。
type writeCounter struct {
	total      int64 // 总大小
	downloaded int64 // 已下载的字节数
	onProgress func(downloaded int64, total int64)
}

// Write实现了io.Writer接口。
//
// 总是完成并且不返回错误。
func (wc *writeCounter) Write(p []byte) (n int, e error) {
	n = len(p)
	wc.downloaded += int64(n)
	wc.onProgress(wc.downloaded, wc.total)
	return
}

func newWriter(size int64, onProgress func(downloaded, total int64)) io.Writer {
	return &writeCounter{total: size, onProgress: onProgress}
}

func main() {
	client := http.DefaultClient
	url := "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4"
	saveTo := "/Users/tin/Desktop/ForBiggerFun.mp4"

	download(client, url, saveTo, func(downloaded, total int64) {
		fmt.Printf("已下载 %d 字节,共 %d\n", downloaded, total)
	})
}

func download(client *http.Client, url, filePath string, onProgress func(downloaded, total int64)) (err error) {
	// 创建文件写入器
	file, err := os.Create(filePath)
	if err != nil {
		return
	}
	defer file.Close()

	// 获取文件大小
	resp, err := client.Head(url)
	if err != nil {
		return
	}
	contentLength := resp.Header.Get("content-length")
	length, err := strconv.Atoi(contentLength)
	if err != nil {
		return
	}

	// 发起请求
	resp, err = client.Get(url)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	// 管道传输流
	body := io.TeeReader(resp.Body, newWriter(int64(length), onProgress))
	_, err = io.Copy(file, body)
	return err
}
英文:

Other answers have explained about PassThru. Just provide a full example with callback function base on Dave Jack's answer.

package main

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

// writeCounter counts the number of bytes written to it.
type writeCounter struct {
	total      int64 // total size
	downloaded int64 // downloaded # of bytes transferred
	onProgress func(downloaded int64, total int64)
}

// Write implements the io.Writer interface.
//
// Always completes and never returns an error.
func (wc *writeCounter) Write(p []byte) (n int, e error) {
	n = len(p)
	wc.downloaded += int64(n)
	wc.onProgress(wc.downloaded, wc.total)
	return
}

func newWriter(size int64, onProgress func(downloaded, total int64)) io.Writer {
	return &writeCounter{total: size, onProgress: onProgress}
}

func main() {
	client := http.DefaultClient
	url := "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4"
	saveTo := "/Users/tin/Desktop/ForBiggerFun.mp4"

	download(client, url, saveTo, func(downloaded, total int64) {
		fmt.Printf("Downloaded %d bytes for a total of %d\n", downloaded, total)
	})
}

func download(client *http.Client, url, filePath string, onProgress func(downloaded, total int64)) (err error) {
	// Create file writer
	file, err := os.Create(filePath)
	if err != nil {
		return
	}
	defer file.Close()

	// Determinate the file size
	resp, err := client.Head(url)
	if err != nil {
		return
	}
	contentLength := resp.Header.Get("content-length")
	length, err := strconv.Atoi(contentLength)
	if err != nil {
		return
	}

	// Make request
	resp, err = client.Get(url)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	// pipe stream
	body := io.TeeReader(resp.Body, newWriter(int64(length), onProgress))
	_, err = io.Copy(file, body)
	return err
}

答案5

得分: 1

Base @Dave Jack

我添加了进度值和从NC(直接TCP数据传输)接收文件数据的功能。

// WriteCounter用于计算写入的字节数。
type WriteCounter struct {
Total      int64 // 总传输字节数
Last       int64
LastUpdate time.Time
}
// Write实现了io.Writer接口。
//
// 总是完成并且不返回错误。
func (wc *WriteCounter) Write(p []byte) (int, error) {
n := len(p)
wc.Total += int64(n)
now := time.Now()
duration := now.Sub(wc.LastUpdate).Seconds()
if duration > 1 {
wc.LastUpdate = now
rate := float64(wc.Total-wc.Last) / (duration) / 1024.0
wc.Last = wc.Total
fmt.Printf("读取了 %d 字节,总共 %d 字节,速率 %.1fKb/s \n", n, wc.Total, rate)
}
return n, nil
}
func Server(dest string) {
outputFile, err := os.Create(dest)
if err != nil {
fmt.Println(err)
}
defer outputFile.Close()
fileWriter := bufio.NewWriter(outputFile)
serverListener, err := net.Listen("tcp", "0.0.0.0:"+PORT)
if err != nil {
fmt.Println(err)
}
defer serverListener.Close()
serverConn, err := serverListener.Accept()
if err != nil {
fmt.Println(err)
}
defer serverConn.Close()
wc := &WriteCounter{}
reader := io.TeeReader(serverConn, wc)
serverConnReader := bufio.NewReaderSize(reader, 32*1024*1024)
io.Copy(fileWriter, serverConnReader)
fileWriter.Flush()
outputFile.Sync()
fmt.Println("完成:写入器")
}
英文:

Base @Dave Jack

I add progress value and receiving file data from NC (direct TCP data transfer)

// WriteCounter counts the number of bytes written to it.
type WriteCounter struct {
Total      int64 // Total # of bytes transferred
Last       int64
LastUpdate time.Time
}
// Write implements the io.Writer interface.
//
// Always completes and never returns an error.
func (wc *WriteCounter) Write(p []byte) (int, error) {
n := len(p)
wc.Total += int64(n)
now := time.Now()
duration := now.Sub(wc.LastUpdate).Seconds()
if duration > 1 {
wc.LastUpdate = now
rate := float64(wc.Total-wc.Last) / (duration) / 1024.0
wc.Last = wc.Total
fmt.Printf("Read %d bytes for a total of %d , Rate  %.1fKb/s \n", n, wc.Total, rate)
}
return n, nil
}
func Server(dest string) {
outputFile, err := os.Create(dest)
if err != nil {
fmt.Println(err)
}
defer outputFile.Close()
fileWriter := bufio.NewWriter(outputFile)
serverListener, err := net.Listen("tcp", "0.0.0.0:"+PORT)
if err != nil {
fmt.Println(err)
}
defer serverListener.Close()
serverConn, err := serverListener.Accept()
if err != nil {
fmt.Println(err)
}
defer serverConn.Close()
wc := &WriteCounter{}
reader := io.TeeReader(serverConn, wc)
serverConnReader := bufio.NewReaderSize(reader, 32*1024*1024)
io.Copy(fileWriter, serverConnReader)
fileWriter.Flush()
outputFile.Sync()
fmt.Println("Done: Writer")
}

huangapple
  • 本文由 发表于 2014年3月15日 16:20:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/22421375.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定