跟踪并显示下载文件的摘要(以百分比形式)- Go语言

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

Track and show downloading file summary ( in percentage ) - Go lang

问题

我正在进行一个通过传递的URL参数下载文件的过程。下载本身是正常进行的,但我无法打印下载完成的百分比摘要(每秒钟一次)。

我已经构建了一个模拟的摘要,但它并没有下载任何内容,只是展示了我想要的效果。

我尝试将io.copy放在我的源代码中,这样我就可以在复制过程中进行更改并打印,但是失败了。

有人可以帮助我吗?
谢谢

package main

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

func downloadFromUrl(url string) {
	tokens := strings.Split(url, "/")
	fileName := tokens[len(tokens)-1]
	fmt.Println("Downloading", url, "to", fileName)

	//创建文件
	output, err := os.Create(fileName)
	if err != nil {
		fmt.Println("Error while creating", fileName, "-", err)
		return
	}
	fmt.Println("Creating", fileName)
	defer output.Close()

	//获取URL
	response, err := http.Get(url)
	if err != nil {
		fmt.Println("Error while downloading", url, "-", err)
		return
	}
	defer response.Body.Close()

	//复制和字节
	n, err := io.Copy(output, response.Body)
	if err != nil {
		fmt.Println("Error while downloading", url, "-", err)
		return
	}

	//跟踪进度
	for i := 1; float64(i) < float64(n); i++ {
		fmt.Println("", float64(i), float64(n))
		percent := float64(i) / (float64(n) / float64(100))
		percentTot := fmt.Sprintf(" %.02f%% ", percent)
		fmt.Println(percentTot, "downloaded\n")
		//time.Sleep(3000 * time.Millisecond)
	}
}

func main() {
	//读取参数
	args := os.Args

	//循环参数
	for i := 1; i < len(args); i++ {
		url := args[i]
		//调用下载
		downloadFromUrl(url)
	}
}

输出:
go run main.go http://download.geonames.org/export/dump/AD.zip
Downloading http://download.geonames.org/export/dump/AD.zip to AD.zip
Creating AD.zip
1 64639
0.00% downloaded

2 64639
0.00% downloaded

3 64639
0.00% downloaded

4 64639
0.01% downloaded

5 64639
0.01% downloaded

英文:

I'm doing a process that downloads a file via a url parameter passed. download is being done properly but what i cant do is to print a summary of the percentage of download done. ( every second )

ive built one simulated kind of this summary but its not download anything, its just to show how i want it.

i tried to bring io.copy inside my source so i could change it ant print while the copy is being done, but it fails.

can someone help me?
thanks

 package main
import (
&quot;fmt&quot;
&quot;io&quot;
&quot;net/http&quot;
&quot;os&quot;
&quot;strings&quot;
//	&quot;time&quot;
)
func downloadFromUrl(url string) {
tokens := strings.Split(url, &quot;/&quot;)
fileName := tokens[len(tokens)-1]
fmt.Println(&quot;Downloading&quot;, url, &quot;to&quot;, fileName)
//create file
output, err := os.Create(fileName)
if err != nil {
fmt.Println(&quot;Error while creating&quot;, fileName, &quot;-&quot;, err)
return
}
fmt.Println(&quot;Creating&quot;, fileName)
defer output.Close()
//get url
response, err := http.Get(url)
if err != nil {
fmt.Println(&quot;Error while downloading&quot;, url, &quot;-&quot;, err)
return
}			
defer response.Body.Close()
//copy and bytes		
n, err := io.Copy(output, response.Body)
if err != nil {
fmt.Println(&quot;Error while downloading&quot;, url, &quot;-&quot;, err)
return
}
//track progress
for i := 1; float64(i) &lt; float64(n); i++ {
fmt.Println(&quot;&quot;,float64(i),float64(n))
percent := float64(i) / (float64(n) / float64(100))
percentTot := fmt.Sprintf(&quot; %#.02f%% &quot;, percent)
fmt.Println(percentTot,&quot;downloaded\n&quot;)
//time.Sleep(3000 * time.Millisecond)
}	
}
func main() {
//read args	
args := os.Args
//loop args
for i := 1; i &lt; len(args); i++ {
url := args[i]
//call download			
downloadFromUrl(url)		
}
}
**Output:**
go run main.go http://download.geonames.org/export/dump/AD.zip
Downloading http://download.geonames.org/export/dump/AD.zip to AD.zip
Creating AD.zip
1 64639
0.00%  downloaded
2 64639
0.00%  downloaded
3 64639
0.00%  downloaded
4 64639
0.01%  downloaded
5 64639
0.01%  downloaded

答案1

得分: 8

我在我的senvgo项目中使用了io.Reader包装器来完成这个任务。

// PassThru包装现有的io.Reader。
//
// 它简单地转发Read()调用,同时显示每次调用的结果。
type PassThru struct {
	io.Reader
	total    int64   // 总传输字节数
	length   int64   // 预期长度
	progress float64 // 进度
}

// Read '覆盖'了底层io.Reader的Read方法。
// 这是io.Copy()将调用的方法。我们只是使用它来跟踪字节计数,然后转发调用。
func (pt *PassThru) Read(p []byte) (int, error) {
	n, err := pt.Reader.Read(p)
	if n > 0 {
		pt.total += int64(n)
		percentage := float64(pt.total) / float64(pt.length) * float64(100)
		i := int(percentage / float64(10))
		is := fmt.Sprintf("%v", i)
		if percentage-pt.progress > 2 {
			fmt.Fprintf(os.Stderr, is)
			pt.progress = percentage
		}
	}

	return n, err
}

// 使用该io.Reader来读取Response(您的http请求的结果):
client := &http.Client{
	CheckRedirect: redirectPolicyFunc,
}
response, err := client.Get("http://example.com/something/large.zip")
defer response.Body.Close()
readerpt := &PassThru{Reader: response.Body, length: response.ContentLength}
body, err := ioutil.ReadAll(readerpt)

ReadAll()调用触发了实际的下载过程,而PassThru.Read()会打印已下载的百分比(相对于预期长度response.ContentLength)。

灵感来源:

英文:

I have done that in my senvgo project with a io.Reader wrapper

// 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
length   int64 // Expected length
progress float64
}

You define a Read() method on that wrapper (to make it an io.Reader)

// Read &#39;overrides&#39; the underlying io.Reader&#39;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)
if n &gt; 0 {
pt.total += int64(n)
percentage := float64(pt.total) / float64(pt.length) * float64(100)
i := int(percentage / float64(10))
is := fmt.Sprintf(&quot;%v&quot;, i)
if percentage-pt.progress &gt; 2 {
fmt.Fprintf(os.Stderr, is)
pt.progress = percentage
}
}
return n, err
}

And you use that io.Reader to read the Response (result of your http request):

client := &amp;http.Client{
CheckRedirect: redirectPolicyFunc,
}
response, err := client.Get(&quot;http://example.com/something/large.zip&quot;)
defer response.Body.Close()
readerpt := &amp;PassThru{Reader: response.Body, length: response.ContentLength}
body, err := ioutil.ReadAll(readerpt)

The ReadAll() call triggers the actual download, and the PassThru.Read() will print the % downloaded against the expected length (response.ContentLength)


Inspirations:

huangapple
  • 本文由 发表于 2014年9月3日 21:14:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/25645363.html
匿名

发表评论

匿名网友

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

确定