Golang和本地Web界面用于下载进度?

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

Golang and local web-interface for download progress?

问题

我是你的中文翻译助手,以下是你提供的代码的翻译:

我刚开始学习Go,并尝试制作一个可以在多个浏览器上下载多个URL并显示进度条的跨浏览器应用程序。Grab包可以很好地完成这个任务,如下面的示例所示。现在,我想要一个自包含/可移植/单个可执行文件的Web-UI,可以在Web浏览器中显示下面代码中的下载进度。

package main

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

func main() {
	// 从命令行参数获取要下载的URL
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "usage: %s url 
...\n"
, os.Args[0])
os.Exit(1) } urls := os.Args[1:] // 同时开始下载文件,每次最多3个 fmt.Printf("正在下载 %d 个文件...\n", len(urls)) respch, err := grab.GetBatch(3, ".", urls...) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } // 启动一个定时器,每200毫秒更新一次进度 t := time.NewTicker(200 * time.Millisecond) // 监控下载进度 completed := 0 inProgress := 0 responses := make([]*grab.Response, 0) for completed < len(urls) { select { case resp := <-respch: // 收到新的响应并开始下载 // (当通道被grab关闭时,会收到nil) if resp != nil { responses = append(responses, resp) } case <-t.C: // 清除行 if inProgress > 0 { fmt.Printf("\033[%dA\033[K", inProgress) } // 更新已完成的下载 for i, resp := range responses { if resp != nil && resp.IsComplete() { // 打印最终结果 if resp.Error != nil { fmt.Fprintf(os.Stderr, "下载 %s 时出错:%v\n", resp.Request.URL(), resp.Error) } else { fmt.Printf("已完成 %s %d / %d 字节 (%d%%)\n", resp.Filename, resp.BytesTransferred(), resp.Size, int(100*resp.Progress())) } // 标记为已完成 responses[i] = nil completed++ } } // 更新进行中的下载 inProgress = 0 for _, resp := range responses { if resp != nil { inProgress++ fmt.Printf("正在下载 %s %d / %d 字节 (%d%%)\033[K\n", resp.Filename, resp.BytesTransferred(), resp.Size, int(100*resp.Progress())) } } } } t.Stop() fmt.Printf("%d 个文件下载成功。\n", len(urls)) }

希望对你有帮助!如果有任何问题,请随时问我。

英文:

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 (
&quot;fmt&quot;
&quot;github.com/cavaliercoder/grab&quot;
&quot;os&quot;
&quot;time&quot;
)
func main() {
// get URL to download from command args
if len(os.Args) &lt; 2 {
fmt.Fprintf(os.Stderr, &quot;usage: %s url 
...\n&quot;, os.Args[0]) os.Exit(1) } urls := os.Args[1:] // start file downloads, 3 at a time fmt.Printf(&quot;Downloading %d files...\n&quot;, len(urls)) respch, err := grab.GetBatch(3, &quot;.&quot;, urls...) if err != nil { fmt.Fprintf(os.Stderr, &quot;%v\n&quot;, 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 &lt; len(urls) { select { case resp := &lt;-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 &lt;-t.C: // clear lines if inProgress &gt; 0 { fmt.Printf(&quot;\033[%dA\033[K&quot;, inProgress) } // update completed downloads for i, resp := range responses { if resp != nil &amp;&amp; resp.IsComplete() { // print final result if resp.Error != nil { fmt.Fprintf(os.Stderr, &quot;Error downloading %s: %v\n&quot;, resp.Request.URL(), resp.Error) } else { fmt.Printf(&quot;Finished %s %d / %d bytes (%d%%)\n&quot;, 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(&quot;Downloading %s %d / %d bytes (%d%%)\033[K\n&quot;, resp.Filename, resp.BytesTransferred(), resp.Size, int(100*resp.Progress())) } } } } t.Stop() fmt.Printf(&quot;%d files successfully downloaded.\n&quot;, len(urls)) }

答案1

得分: 2

一种解决方案是使用 WebSocket,因为它使用一种特殊的头部来建立连接,从而将浏览器和服务器之间所需的握手次数减少到只有一次,这意味着客户端和服务器之间的通信不会阻塞。

这个连接将在其生命周期内保持活动状态,你可以使用 JavaScript 来写入或读取来自该连接的数据,就像使用传统的 TCP sockets 一样。

在实现方面,你可以将 grab 包生成的信息编码为一个 json 字符串,然后使用 websocket.JSON.Send 来传输这个字符串。

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

在客户端部分,你可以获取组合的 JSON 对象,然后根据你的使用目的和首选技术(canvas、DOM 等)进行解析和使用。

下面是一个 WebSocket 服务器端的代码片段:

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
}
// 如果 BytesTransferred == 100,跳出循环
}
}
func main() {
http.Handle("/", websocket.Handler(Echo))
if err := http.ListenAndServe(":1234", nil); err != nil {
fmt.Println("Cannot initiate a WebSocket connection")
}
}

当然,这些值是硬编码的,如果你希望改变传输速度,可以使用定时器。

客户端的代码使用 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 message: " + 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)
}
}

在 JavaScript 中,你可以连接到 WebSocket 并接收数据:

<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);
// 反序列化 JSON 数据
var json = JSON.parse(e.data);
}
};
function send() {
var msg = document.getElementById('message').value;
sock.send(msg);
};
</script>
英文:

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

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

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

&lt;script type=&quot;text/javascript&quot;&gt;
var sock = null;
var wsuri = &quot;ws://127.0.0.1:1234&quot;;
window.onload = function() {
console.log(&quot;onload&quot;);
sock = new WebSocket(wsuri);
sock.onopen = function() {
console.log(&quot;connected to &quot; + wsuri);
}
sock.onclose = function(e) {
console.log(&quot;connection closed (&quot; + e.code + &quot;)&quot;);
}
sock.onmessage = function(e) {
console.log(&quot;message received: &quot; + e.data);
// deserialize json data
var json = JSON.parse(e.data);
}
};
function send() {
var msg = document.getElementById(&#39;message&#39;).value;
sock.send(msg);
};
&lt;/script&gt;

huangapple
  • 本文由 发表于 2016年3月25日 17:32:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/36217363.html
匿名

发表评论

匿名网友

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

确定