douqiaosu0280 2016-03-25 09:32
浏览 56
已采纳

Golang和本地网络界面的下载进度?

I am new to Go and trying to make a cross-browser app which can download multiple url's with progress bar. The Grab package does the job nicely as shown in the example below. Now, I want to have a self-contained/portable/single-executable Web-UI which can display the download progress from the below code in a web-browser?

package main

import (
    "fmt"
    "github.com/cavaliercoder/grab"
    "os"
    "time"
)

func main() {
    // get URL to download from command args
    if len(os.Args) < 2 {
        fmt.Fprintf(os.Stderr, "usage: %s url [url]...
", os.Args[0])
        os.Exit(1)
    }

    urls := os.Args[1:]

    // start file downloads, 3 at a time
    fmt.Printf("Downloading %d files...
", len(urls))
    respch, err := grab.GetBatch(3, ".", urls...)
    if err != nil {
        fmt.Fprintf(os.Stderr, "%v
", err)
        os.Exit(1)
    }

    // start a ticker to update progress every 200ms
    t := time.NewTicker(200 * time.Millisecond)

    // monitor downloads
    completed := 0
    inProgress := 0
    responses := make([]*grab.Response, 0)
    for completed < len(urls) {
        select {
        case resp := <-respch:
            // a new response has been received and has started downloading
            // (nil is received once, when the channel is closed by grab)
            if resp != nil {
                responses = append(responses, resp)
            }

        case <-t.C:
            // clear lines
            if inProgress > 0 {
                fmt.Printf("\033[%dA\033[K", inProgress)
            }

            // update completed downloads
            for i, resp := range responses {
                if resp != nil && resp.IsComplete() {
                    // print final result
                    if resp.Error != nil {
                        fmt.Fprintf(os.Stderr, "Error downloading %s: %v
", resp.Request.URL(), resp.Error)
                    } else {
                        fmt.Printf("Finished %s %d / %d bytes (%d%%)
", resp.Filename, resp.BytesTransferred(), resp.Size, int(100*resp.Progress()))
                    }

                    // mark completed
                    responses[i] = nil
                    completed++
                }
            }

            // update downloads in progress
            inProgress = 0
            for _, resp := range responses {
                if resp != nil {
                    inProgress++
                    fmt.Printf("Downloading %s %d / %d bytes (%d%%)\033[K
", resp.Filename, resp.BytesTransferred(), resp.Size, int(100*resp.Progress()))
                }
            }
        }
    }

    t.Stop()

    fmt.Printf("%d files successfully downloaded.
", len(urls))
}
  • 写回答

1条回答 默认 最新

  • dongluxin2452 2016-03-25 14:16
    关注

    One kind of solution would be to use websocket, because to establish a connection it uses a special kind of header that reduces the number of handshakes required between browser and server to only one, which means the client server communication will not block.

    This connection will remain active throughout its lifetime, and you can use JavaScript to write or read data from this connection, as in the case of a conventional TCP sockets.

    As a matter of implementation you can encode the resulting information composed by grab package as a json string then you can transmit this string with websocket.JSON.Send.

    func (cd Codec) Send(ws *Conn, v interface{}) (err error)
    

    And on the client part you can obtain the composed json object which later you can parse and use as you wish, depending on your purpose of use and the technology prefered (canvas, DOM etc.).

    Below is a snippet of websocket server side:

    package main
    
    import (
        "fmt"
        "golang.org/x/net/websocket"
        "net/http"
    )
    
    type FileInformation struct {
        BytesTransferred int
        Size             string
        Filename         string
        Progress         int
    }
    
    func Echo(ws *websocket.Conn) {
        info := FileInformation{
            BytesTransferred: 70,
            Size:             "1.7MB",
            Filename:         "test.txt",
            Progress:         60,
        }
    
        for {
            if err := websocket.JSON.Send(ws, info); err != nil {
                fmt.Println("Error sending message")
                break
            }
            // if BytesTransferred == 100 break
        }
    }
    
    func main() {
        http.Handle("/", websocket.Handler(Echo))
    
        if err := http.ListenAndServe(":1234", nil); err != nil {
            fmt.Println("Cannot iniatiate a websocket connection")
        }
    }
    

    Of course the values are hard coded and you can use a timer tick if you wish to alter the transfer speed.

    The client side written in go:

    package main
    
    import (
        "fmt"
        "golang.org/x/net/websocket"
        "io"
        "os"
    )
    
    func main() {
        type FileInformation struct {
            BytesTransferred int
            Size             string
            Filename         string
            Progress         int
        }
    
        if len(os.Args) > 1 && (os.Args[1] == "--help" || os.Args[1] == "-h") {
            fmt.Println("Usage : " + os.Args[0] + " ws://localhost:port")
            os.Exit(1)
        }
    
        conn, err := websocket.Dial(os.Args[1], "", "http://127.0.0.1")
        checkError(err)
        var info FileInformation
    
        for {
            err := websocket.JSON.Receive(conn, &info)
            if err != nil {
                if err == io.EOF {
                    break
                }
                fmt.Println("Couldn't receive msg " + err.Error())
                break
            }
            fmt.Printf("%s", info)
    
            if err := websocket.JSON.Send(conn, info); err != nil {
                checkError(err)
            }
        }
    }
    
    func checkError(err error) {
        if err != nil {
            fmt.Println("Fatal error: " + err.Error())
            os.Exit(1)
        }
    }
    

    In javascript you can connect to websocket and receive the data as:

    <script type="text/javascript">
        var sock = null;
        var wsuri = "ws://127.0.0.1:1234";
    
        window.onload = function() {
    
            console.log("onload");
    
            sock = new WebSocket(wsuri);
    
            sock.onopen = function() {
                console.log("connected to " + wsuri);
            }
    
            sock.onclose = function(e) {
                console.log("connection closed (" + e.code + ")");
            }
    
            sock.onmessage = function(e) {
                console.log("message received: " + e.data);
                // deserialize json data
                var json = JSON.parse(e.data);
            }
        };
    
        function send() {
            var msg = document.getElementById('message').value;
            sock.send(msg);
        };
    </script>
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 DS18B20内部ADC模数转换器
  • ¥15 做个有关计算的小程序
  • ¥15 MPI读取tif文件无法正常给各进程分配路径
  • ¥15 如何用MATLAB实现以下三个公式(有相互嵌套)
  • ¥30 关于#算法#的问题:运用EViews第九版本进行一系列计量经济学的时间数列数据回归分析预测问题 求各位帮我解答一下
  • ¥15 setInterval 页面闪烁,怎么解决
  • ¥15 如何让企业微信机器人实现消息汇总整合
  • ¥50 关于#ui#的问题:做yolov8的ui界面出现的问题
  • ¥15 如何用Python爬取各高校教师公开的教育和工作经历
  • ¥15 TLE9879QXA40 电机驱动