英文:
What causes browsers to open "Save As" window when downloading dynamic content?
问题
我正在尝试一种想法,将动态数据从Web服务器流式传输到客户端设备上的文件中。为了实现这个想法,我使用了HTTP Content-Disposition响应头和HTML download属性。以下是我的示例代码,其中服务器使用Go实现:
HTML:
<a href="download" download>Download</a>
Server:
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
// 处理下载请求。
http.HandleFunc("/download", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Disposition", "attachment; filename=\"log.txt\"")
w.Write([]byte("first message\n"))
time.Sleep(10 * time.Second)
// 在我的开发机上,以下for循环大约需要30秒才能运行完毕。
for i := 0; i < 1000000; i++ {
timestamp := time.Now().Unix()
log(timestamp)
w.Write([]byte(fmt.Sprintf("%v\n", timestamp)))
time.Sleep(time.Microsecond)
}
log("done")
})
// 启动HTTP服务器。
if err := http.ListenAndServe(":8080", nil); err != nil {
log(err)
}
}
func log(v ...interface{}) {
fmt.Println(v...)
}
这个示例代码可以成功下载从download
处理程序中的所有内容。然而,我观察到以下行为,我无法解释:
我将浏览器配置为始终询问下载文件的保存位置。当运行上述示例代码时,Chrome只在10秒的休眠之后但在for循环完成之前以及处理程序函数返回之前打开“另存为”窗口。为什么Chrome在发送“first message”之后的10秒休眠之前没有弹出“另存为”窗口?导致“另存为”窗口只在for循环开始时打开的消息与在for循环中发送的消息有什么不同?
附注:如果FileSystemWritableFileStream在跨浏览器上有更好的支持,我会使用它将动态服务器数据直接流式传输到客户端的文件中。
英文:
I am experimenting with an idea to stream dynamic data from a web server into a file on the client device. To implement this idea, I am making use of the HTTP Content-Disposition response header and the HTML download attribute. The following is my sample code, where the server is implemented in Go:
HTML:
<a href="download" download>Download</a>
Server:
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
// Handle download request.
http.HandleFunc("/download", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Disposition", "attachment; filename=\"log.txt\"")
w.Write([]byte("first message\n"))
time.Sleep(10 * time.Second)
// The following for-loop takes about 30 seconds to run on my dev machine.
for i := 0; i < 1000000; i++ {
timestamp := time.Now().Unix()
log(timestamp)
w.Write([]byte(fmt.Sprintf("%v\n", timestamp)))
time.Sleep(time.Microsecond)
}
log("done")
})
// Start HTTP server.
if err := http.ListenAndServe(":8080", nil); err != nil {
log(err)
}
}
func log(v ...interface{}) {
fmt.Println(v...)
}
This sample code works in that it successfully downloads all the content from the download
handler when clicking on the "Download" link. However, I am observing the following behavior that I am unable to explain:
I have my browser configured to always ask where to save the downloaded files. When running the sample code above, Chrome opens the Save As window only after the 10 second sleep but before the for-loop is complete and in turn the handler function has returned. Why did Chrome not present the Save As window when the "first message" was sent before the 10 second sleep? What is different between the "first message" and the messages being sent in the for-loop that causes the Save As window to only open when the for-loop starts?
Aside: If FileSystemWritableFileStream had greater cross-browser support, I'd use that to stream dynamic server data directly into a file on the client side.
答案1
得分: 1
Go的http.ResponseWriter
在Transport级别上有一个默认的4KB缓冲区:
type Transport struct {
// ...
// WriteBufferSize指定在写入传输时使用的写入缓冲区的大小。
// 如果为零,则使用默认值(当前为4KB)。
WriteBufferSize int
// ...
}
在某些情况下,当使用标准响应时,您可以通过使用http.Flusher
接口的类型断言来使用Flush
方法立即发送字节:
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
英文:
Go's http.ResponseWriter
has a default 4KB buffer, defined at the Transport level:
type Transport struct {
// ...
// WriteBufferSize specifies the size of the write buffer used
// when writing to the transport.
// If zero, a default (currently 4KB) is used.
WriteBufferSize int
// ...
}
In some instances, when using standard responses, you can make use the Flush
method by using type assertion with the http.Flusher
interface to send the bytes right away:
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
答案2
得分: 0
要让Firefox在发送"first message"后立即打开"另存为"窗口,似乎只需要使用Ricardo Souza的答案。要让Chrome执行相同的操作,还需要将响应的Content-Type
头设置为除默认值text/plain
以外的任何值(感谢此SO答案)。示例代码如下:
w.Header().Set("Content-Type", "application/octet-stream; charset=utf-8")
英文:
To have Firefox open the Save As window immediately after "first message" is sent, Ricardo Souza's answer appears to be all that's needed. To have Chrome do the same, the response's Content-Type
header also needs to be set to anything other than the default text/plain
(thanks to this SO answer). Example:
w.Header().Set("Content-Type", "application/octet-stream; charset=utf-8")
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论