如何从Go服务器上的浏览器中下载文件

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

How to download file in browser from Go server

问题

我的代码从远程URL获取文件并在浏览器中下载文件:

func Index(w http.ResponseWriter, r *http.Request) {
    url := "http://upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png"

    ...

    resp, err := client.Get(url)
    if err != nil {
        fmt.Println(err)
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
    }

    fmt.Println(len(body))
    // 在浏览器中下载文件

}

func main() {
    http.HandleFunc("/", Index)
    err := http.ListenAndServe(":8000", nil)

    if err != nil {
        fmt.Println(err)
    }
}

代码:http://play.golang.org/p/x-EyR2zFjv

获取文件是可以的,但如何在浏览器中下载它呢?

英文:

My code get file from remote url and download file in browser:

func Index(w http.ResponseWriter, r *http.Request) {
	url := "http://upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png"

	...
	
    resp, err := client.Get(url)
	if err != nil {
		fmt.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(len(body))
    //download the file in browser
    
}

func main() {
	http.HandleFunc("/", Index)
	err := http.ListenAndServe(":8000", nil)

	if err != nil {
		fmt.Println(err)
	}
}

code: http://play.golang.org/p/x-EyR2zFjv

Get file is ok, but how to downloaded it in browser?

答案1

得分: 89

为了使浏览器打开下载对话框,需要在响应中添加Content-DispositionContent-Type头部:

w.Header().Set("Content-Disposition", "attachment; filename=WHATEVER_YOU_WANT")
w.Header().Set("Content-Type", r.Header.Get("Content-Type"))

在将内容发送给客户端之前执行此操作。您可能还希望将响应的Content-Length头部复制到客户端,以显示正确的进度。

要将响应主体流式传输到客户端而不完全加载到内存中(对于大文件很重要),只需将主体读取器复制到响应写入器中:

io.Copy(w, resp.Body)

io.Copy是一个非常有用的小函数,它接受一个读取器接口和一个写入器接口,从一个接口读取数据并将其写入另一个接口。对于这种情况非常有用!

我已经修改了您的代码以实现这一点:http://play.golang.org/p/v9IAu2Xu3_

英文:

To make the browser open the download dialog, add a Content-Disposition and Content-Type headers to the response:

w.Header().Set("Content-Disposition", "attachment; filename=WHATEVER_YOU_WANT")
w.Header().Set("Content-Type", r.Header.Get("Content-Type"))

Do this BEFORE sending the content to the client. You might also want to copy the Content-Length header of the response to the client, to show proper progress.

To stream the response body to the client without fully loading it into memory (for big files this is important) - simply copy the body reader to the response writer:

io.Copy(w, resp.Body)

io.Copy is a nice little function that take a reader interface and writer interface, reads data from one and writes it to the other. Very useful for this kind of stuff!

I've modified your code to do this: http://play.golang.org/p/v9IAu2Xu3_

答案2

得分: 12

如果你已经在磁盘上有该文件,只需使用http.ServeFile()。它会自动处理Content-Length,以便浏览器可以显示下载进度。

w.Header().Set("Content-Disposition", "attachment; filename="+strconv.Quote(filename))
w.Header().Set("Content-Type", "application/octet-stream")
http.ServeFile(w, r, filePath)
英文:

In case you already have the file on disk, just use http.ServeFile(). It automatically handles Content-Length so that the browser can display a download progress.

w.Header().Set("Content-Disposition", "attachment; filename="+strconv.Quote(filename))
w.Header().Set("Content-Type", "application/octet-stream")
http.ServeFile(w, r, filePath)

答案3

得分: 3

你需要使用Content-Disposition头部,并将其值设置为attachment。MDN Web文档对该头部的说明如下:

[...] Content-Disposition响应头部用于指示内容是内联显示(即作为网页的一部分在浏览器中显示),还是作为附件下载并保存到本地

如果你想为客户端指定一个文件名,可以使用filename指令。格式如下:

Content-Disposition: attachment; filename="filename.jpg"

这是一个可选参数,并且有一些限制。

[...] 文件名始终是可选的,应用程序不应盲目使用它:应该去除路径信息,并进行服务器文件系统规则的转换。[...]

为了正确格式化头部中的文件名,你可以使用mime.FormatMediaType函数。示例:

cd := mime.FormatMediaType("attachment", map[string]string{"filename": d.Name()})
w.Header().Set("Content-Disposition", cd)

在这种情况下,你可以使用application/octet-stream作为内容类型,因为浏览器不需要知道响应的MIME类型

[...] 通用的二进制数据(或其真实类型未知的二进制数据)使用application/octet-stream。[...]

w.Header().Set("Content-Type", "application/octet-stream")

对于输出文件内容,我建议使用http.ServeContenthttp.ServeFile,因为它们可以直接处理RFC 7233 - 范围请求。示例:

f, err := fs.Open(name)
// [...]
cd := mime.FormatMediaType("attachment", map[string]string{"filename": d.Name()})
w.Header().Set("Content-Disposition", cd)
w.Header().Set("Content-Type", "application/octet-stream")
http.ServeContent(w, r, d.Name(), d.ModTime(), f)
英文:

What you need to use is the Content-Disposition header with attachment value. MDN web docs says the following about the header:

> [...], the Content-Disposition response header is a header indicating if the content is expected to be displayed inline in the browser, that is, as a Web page or as part of a Web page, or as an attachment, that is downloaded and saved locally.

If you want to specify a filename for the clients you can use the filename directive. In the following format:

Content-Disposition: attachment; filename="filename.jpg"

This is an optional parameter and it has some restrictions.

> [...] The filename is always optional and must not be used blindly by the application: path information should be stripped, and conversion to the server file system rules should be done. [...]

To format filename properly in the header you should use mime.FormatMediaType. Example:

cd := mime.FormatMediaType("attachment", map[string]string{"filename": d.Name()})
w.Header().Set("Content-Disposition", cd)

In this case for content type you can use application/octet-stream because the browser does not have to know the MIME type of the response.

> [...] Generic binary data (or binary data whose true type is unknown) is application/octet-stream. [...]

w.Header().Set("Content-Type", "application/octet-stream")

For outputing the content of a. file I would recommend to use http.ServeContent or http.ServeFile because they handle RFC 7233 - Range Requests out of the box. Example:

f, err := fs.Open(name)
// [...]
cd := mime.FormatMediaType("attachment", map[string]string{"filename": d.Name()})
w.Header().Set("Content-Disposition", cd)
w.Header().Set("Content-Type", "application/octet-stream")
http.ServeContent(w, r, d.Name(), d.ModTime(), f)

huangapple
  • 本文由 发表于 2014年6月9日 16:08:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/24116147.html
匿名

发表评论

匿名网友

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

确定