golang动态进度条
golang dynamic progressbar
我正在尝试使用 golang 下载文件。
我正在下载文件,没问题。在我使用 cheggaaa 的进度条库之后。但是我不能动态。
如何实现动态进度条?
我的代码如下:
主要包
import (
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"github.com/cheggaaa/pb"
"time"
)
/*
usage = usage text
version = current number
help use Sprintf
*cliUrl from cmd
*cliVersion from cmd
*cliHelp * from cmd
*/
var (
usage = "Usage: ./gofret -url=http://some/do.zip"
version = "Version: 0.1"
help = fmt.Sprintf("\n\n %s\n\n\n %s", usage, version)
cliUrl *string
cliVersion *bool
cliHelp *bool
)
func init() {
/*
if *cliUrl != "" {
fmt.Println(*cliUrl)
}
./gofret -url=http://somesite.com/somefile.zip
./gofret -url=https://github.com/aligoren/syspy/archive/master.zip
*/
cliUrl = flag.String("url", "", usage)
/*
else if *cliVersion{
fmt.Println(flag.Lookup("version").Usage)
}
./gofret -version
*/
cliVersion = flag.Bool("version", false, version)
/*
if *cliHelp {
fmt.Println(flag.Lookup("help").Usage)
}
./gofret -help
*/
cliHelp = flag.Bool("help", false, help)
}
func main() {
/*
Parse all flags
*/
flag.Parse()
if *cliUrl != "" {
fmt.Println("Downloading file")
/* parse url from *cliUrl */
fileUrl, err := url.Parse(*cliUrl)
if err != nil {
panic(err)
}
/* get path from *cliUrl */
filePath := fileUrl.Path
/*
seperate file.
http://+site.com/+(file.zip)
*/
segments := strings.Split(filePath, "/")
/*
file.zip filename lenth -1
*/
fileName := segments[len(segments)-1]
/*
Create new file.
Filename from fileName variable
*/
file, err := os.Create(fileName)
if err != nil {
fmt.Println(err)
panic(err)
}
defer file.Close()
/*
check status and CheckRedirect
*/
checkStatus := http.Client{
CheckRedirect: func(r *http.Request, via []*http.Request) error {
r.URL.Opaque = r.URL.Path
return nil
},
}
/*
Get Response: 200 OK?
*/
response, err := checkStatus.Get(*cliUrl)
if err != nil {
fmt.Println(err)
panic(err)
}
defer response.Body.Close()
fmt.Println(response.Status) // Example: 200 OK
/*
fileSize example: 12572 bytes
*/
fileSize, err := io.Copy(file, response.Body)
/*
progressbar worked after download :(
*/
var countSize int = int(fileSize/1000)
count := countSize
bar := pb.StartNew(count)
for i := 0; i < count; i++ {
bar.Increment()
time.Sleep(time.Millisecond)
}
bar.FinishPrint("The End!")
if err != nil {
panic(err)
}
fmt.Printf("%s with %v bytes downloaded", fileName, count)
} else if *cliVersion {
/*
lookup version flag's usage text
*/
fmt.Println(flag.Lookup("version").Usage)
} else if *cliHelp {
/*
lookup help flag's usage text
*/
fmt.Println(flag.Lookup("help").Usage)
} else {
/*
using help's usage text for handling other status
*/
fmt.Println(flag.Lookup("help").Usage)
}
}
而我的程序是运行:
Downloading file
200 OK
下载后工作进度条:
6612 / 6612 [=====================================================] 100.00 % 7s
The End!
master.zip with 6612 bytes downloaded
我的进度条代码如下:
/*
progressbar worked after download :(
*/
var countSize int = int(fileSize/1000)
count := countSize
bar := pb.StartNew(count)
for i := 0; i < count; i++ {
bar.Increment()
time.Sleep(time.Millisecond)
}
bar.FinishPrint("The End!")
如何解决进度条问题?
我写了下面的东西,在一般情况下是正确的,与进度条无关,但是这个库完全设计用来处理这个问题,有专门的支持,并给出 explicit example of downloading a file.
您需要运行在更新进度条的同时下载,目前您正在下载整个文件,然后更新进度条。
这有点草率,但应该让你朝着正确的方向前进:
首先,获取预期的文件大小:
filesize := response.ContentLength
然后在 goroutine 中开始下载:
go func() {
n, err := io.Copy(file, response.Body)
if n != filesize {
log.Fatal("Truncated")
}
if err != nil {
log.Fatalf("Error: %v", err)
}
}()
然后更新进度条:
countSize := int(filesize / 1000)
bar := pb.StartNew(countSize)
var fi os.FileInfo
for fi == nil || fi.Size() < filesize {
fi, _ = file.Stat()
bar.Set(int(fi.Size() / 1000))
time.Sleep(time.Millisecond)
}
bar.FinishPrint("The End!")
就像我说的,这有点草率;您可能希望根据文件的大小更好地缩放条形图,并且 log.Fatal
调用很难看。但它处理了问题的核心。
或者,您可以通过编写自己的 io.Copy
版本在没有 goroutine 的情况下做到这一点。从 response.Body
读取一个块,更新进度条,然后将一个块写入 file
。这可以说更好,因为您可以避免睡眠调用。
不需要更多的协程。刚刚从 bar
读取
// start new bar
bar := pb.New(fileSize).SetUnits(pb.U_BYTES)
bar.Start()
// create proxy reader
rd := bar.NewProxyReader(response.Body)
// and copy from reader
io.Copy(file, rd)
其实你可以通过下面的代码自己实现进度条。
func (bar *Bar) Play(cur int64) {
bar.cur = cur
last := bar.percent
bar.percent = bar.getPercent()
if bar.percent != last && bar.percent%2 == 0 {
bar.rate += bar.graph
}
fmt.Printf("\r[%-50s]%3d%% %8d/%d", bar.rate, bar.percent, bar.cur, bar.total)
}
这里的关键是使用 escape \r 这将用创建动态效果的更新进度替换当前进度。
可以在 here 找到详细的解释。
我正在尝试使用 golang 下载文件。
我正在下载文件,没问题。在我使用 cheggaaa 的进度条库之后。但是我不能动态。
如何实现动态进度条?
我的代码如下:
主要包
import (
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"github.com/cheggaaa/pb"
"time"
)
/*
usage = usage text
version = current number
help use Sprintf
*cliUrl from cmd
*cliVersion from cmd
*cliHelp * from cmd
*/
var (
usage = "Usage: ./gofret -url=http://some/do.zip"
version = "Version: 0.1"
help = fmt.Sprintf("\n\n %s\n\n\n %s", usage, version)
cliUrl *string
cliVersion *bool
cliHelp *bool
)
func init() {
/*
if *cliUrl != "" {
fmt.Println(*cliUrl)
}
./gofret -url=http://somesite.com/somefile.zip
./gofret -url=https://github.com/aligoren/syspy/archive/master.zip
*/
cliUrl = flag.String("url", "", usage)
/*
else if *cliVersion{
fmt.Println(flag.Lookup("version").Usage)
}
./gofret -version
*/
cliVersion = flag.Bool("version", false, version)
/*
if *cliHelp {
fmt.Println(flag.Lookup("help").Usage)
}
./gofret -help
*/
cliHelp = flag.Bool("help", false, help)
}
func main() {
/*
Parse all flags
*/
flag.Parse()
if *cliUrl != "" {
fmt.Println("Downloading file")
/* parse url from *cliUrl */
fileUrl, err := url.Parse(*cliUrl)
if err != nil {
panic(err)
}
/* get path from *cliUrl */
filePath := fileUrl.Path
/*
seperate file.
http://+site.com/+(file.zip)
*/
segments := strings.Split(filePath, "/")
/*
file.zip filename lenth -1
*/
fileName := segments[len(segments)-1]
/*
Create new file.
Filename from fileName variable
*/
file, err := os.Create(fileName)
if err != nil {
fmt.Println(err)
panic(err)
}
defer file.Close()
/*
check status and CheckRedirect
*/
checkStatus := http.Client{
CheckRedirect: func(r *http.Request, via []*http.Request) error {
r.URL.Opaque = r.URL.Path
return nil
},
}
/*
Get Response: 200 OK?
*/
response, err := checkStatus.Get(*cliUrl)
if err != nil {
fmt.Println(err)
panic(err)
}
defer response.Body.Close()
fmt.Println(response.Status) // Example: 200 OK
/*
fileSize example: 12572 bytes
*/
fileSize, err := io.Copy(file, response.Body)
/*
progressbar worked after download :(
*/
var countSize int = int(fileSize/1000)
count := countSize
bar := pb.StartNew(count)
for i := 0; i < count; i++ {
bar.Increment()
time.Sleep(time.Millisecond)
}
bar.FinishPrint("The End!")
if err != nil {
panic(err)
}
fmt.Printf("%s with %v bytes downloaded", fileName, count)
} else if *cliVersion {
/*
lookup version flag's usage text
*/
fmt.Println(flag.Lookup("version").Usage)
} else if *cliHelp {
/*
lookup help flag's usage text
*/
fmt.Println(flag.Lookup("help").Usage)
} else {
/*
using help's usage text for handling other status
*/
fmt.Println(flag.Lookup("help").Usage)
}
}
而我的程序是运行:
Downloading file
200 OK
下载后工作进度条:
6612 / 6612 [=====================================================] 100.00 % 7s
The End!
master.zip with 6612 bytes downloaded
我的进度条代码如下:
/*
progressbar worked after download :(
*/
var countSize int = int(fileSize/1000)
count := countSize
bar := pb.StartNew(count)
for i := 0; i < count; i++ {
bar.Increment()
time.Sleep(time.Millisecond)
}
bar.FinishPrint("The End!")
如何解决进度条问题?
我写了下面的东西,在一般情况下是正确的,与进度条无关,但是这个库完全设计用来处理这个问题,有专门的支持,并给出 explicit example of downloading a file.
您需要运行在更新进度条的同时下载,目前您正在下载整个文件,然后更新进度条。
这有点草率,但应该让你朝着正确的方向前进:
首先,获取预期的文件大小:
filesize := response.ContentLength
然后在 goroutine 中开始下载:
go func() {
n, err := io.Copy(file, response.Body)
if n != filesize {
log.Fatal("Truncated")
}
if err != nil {
log.Fatalf("Error: %v", err)
}
}()
然后更新进度条:
countSize := int(filesize / 1000)
bar := pb.StartNew(countSize)
var fi os.FileInfo
for fi == nil || fi.Size() < filesize {
fi, _ = file.Stat()
bar.Set(int(fi.Size() / 1000))
time.Sleep(time.Millisecond)
}
bar.FinishPrint("The End!")
就像我说的,这有点草率;您可能希望根据文件的大小更好地缩放条形图,并且 log.Fatal
调用很难看。但它处理了问题的核心。
或者,您可以通过编写自己的 io.Copy
版本在没有 goroutine 的情况下做到这一点。从 response.Body
读取一个块,更新进度条,然后将一个块写入 file
。这可以说更好,因为您可以避免睡眠调用。
不需要更多的协程。刚刚从 bar
读取// start new bar
bar := pb.New(fileSize).SetUnits(pb.U_BYTES)
bar.Start()
// create proxy reader
rd := bar.NewProxyReader(response.Body)
// and copy from reader
io.Copy(file, rd)
其实你可以通过下面的代码自己实现进度条。
func (bar *Bar) Play(cur int64) {
bar.cur = cur
last := bar.percent
bar.percent = bar.getPercent()
if bar.percent != last && bar.percent%2 == 0 {
bar.rate += bar.graph
}
fmt.Printf("\r[%-50s]%3d%% %8d/%d", bar.rate, bar.percent, bar.cur, bar.total)
}
这里的关键是使用 escape \r 这将用创建动态效果的更新进度替换当前进度。
可以在 here 找到详细的解释。