golang - 如何从golang服务器下载浏览器中的文件?

时间:2014-06-09 08:08:45

标签: go

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

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)
    }
}

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

获取文件没问题,但如何在浏览器中下载?

感谢。

3 个答案:

答案 0 :(得分:52)

要使浏览器打开下载对话框,请在回复中添加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_

答案 1 :(得分:2)

如果文件已经在磁盘上,请使用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)

答案 2 :(得分:0)

您需要使用带有attachment值的Content-Disposition标头。 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 type

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

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

用于输出a的内容。我建议使用http.ServeContenthttp.ServeFile这个文件,因为它们开箱即用地处理RFC 7233 - Range Requests。示例:

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)