如何让用户在生成文件时下载文件

Posted

技术标签:

【中文标题】如何让用户在生成文件时下载文件【英文标题】:How to let user download file while it's being generated 【发布时间】:2019-05-03 00:46:13 【问题描述】:

我想让用户在文件尚未准备好时开始下载它。我不想将用户发送到某个页面说“等待 30 秒,正在准备文件”。我无法提前生成文件。我需要用户单击/发送表单,选择下载位置并开始下载。生成的文件将是 zip,所以我想,应该可以使用 zip 的前几个字节(始终相同)发送文件名,并且在生成文件之前不要确认 tcp 数据包已正确发送或类似然后在生成文件后发送其余的。

我该怎么做?有什么工具可以做到这一点。或者有什么更好的方法吗?更高级别的解决方案更好,C 不是我的强项。最好在 Python 中。谢谢。

正在生成的文件是 zip 文件,在它准备好之前还没有任何东西要发送。基本上根据输入,我生成一组文件(这需要几十秒),然后我将它们压缩并提供给用户。我的应用程序在 linux 上的 python 中,但我将使用哪个服务器并不重要。

【问题讨论】:

这实际上取决于您的服务器语言。这绝对是可能的,是的,但这太宽泛了。您只需要写入 TCP 缓冲区,因为压缩引擎提供了可用的字节,具体实现将根据您的设置而有很大差异。 文件是如何生成的?这是在什么平台上的?基本上你可以开始发送输出,客户端会等待。 文件正在由自定义脚本生成。 【参考方案1】:

在准备文件时,客户端很可能会超时(或等待 30 秒而不通知)。我会在传输过程中使用流压缩算法(gzip)来压缩文件。这不会产生最佳压缩,但会以可预测的方式提供文件。

调查“Content-Encoding: gzip”HTTP 标头。

【讨论】:

【参考方案2】:

通常,像这样的应用程序将分两部分实现,例如票务系统。

    当用户单击/提交表单时,表单将向服务发送请求,该服务将作为后台进程开始生成文件,然后(不等待文件生成)它将生成票证/哈希表示新文件,然后将用户重定向到新 URL,例如/files/<random-hash>

    在这个新的 URL /files/<random-hash> 上,虽然文件还没有准备好,它会返回一个简单的 html 页面,显示消息让用户等待,并且页面上有一个脚本会不断刷新每隔几秒翻页一次。只要文件还没有准备好,它就会一直显示这个消息,但是一旦文件准备好了,这个 URL 就会在它的响应中返回实际的文件内容,并带有适当的 mime-header。

使用数据库和一些编程来实现该解决方案非常简单。不过,如果您正在寻找一种现成的可以使用的工具,我很抱歉我不熟悉这个工具。希望对您有所帮助。

【讨论】:

【参考方案3】:

尽管声称这是不可能的,但我还是设法找到了方法。在此期间我学了一点 Go,所以我使用了它,但我想它在其他语言中不会有太大的不同。

基本上第一个字节被写入写入器,然后被刷新。浏览器比等待休息。

package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "strings"
    "time"
)

func Zip(w http.ResponseWriter, r *http.Request) 
    file_name := r.URL.Path
    file_name = strings.TrimPrefix(file_name, "/files/")

    w.Header().Set("Content-type", "application/zip")
    w.Write([]byte80)
    if f, ok := w.(http.Flusher); ok 
        f.Flush()
    
    for 
        if _, err := os.Stat("./files/" + file_name); err == nil 
            fmt.Println("file found, breaking")
            break
        
        time.Sleep(time.Second)
    
    stream_file_bytes, err := ioutil.ReadFile("./files/" + file_name)
    if err != nil 
        fmt.Println(err)
        return
    

    b := bytes.NewBuffer(stream_file_bytes)
    b.Next(1)
    b.WriteTo(w)
    fmt.Println("end")


func main() 
    http.HandleFunc("/files/", Zip)
    if err := http.ListenAndServe(":8090", nil); err != nil 
        panic(err)
    

【讨论】:

以上是关于如何让用户在生成文件时下载文件的主要内容,如果未能解决你的问题,请参考以下文章

使用 AsyncTask 显示进度时下载文件时出错

iPhone MPMoviePlayerController :在流式传输时下载文件并在本地播放它们

如何在生成文件中动态重命名目标文件

应用在后台时下载网络数据

使用模式规则在生成文件中合并相关文件

使用变量包含参数在生成文件中添加 Windows 包含路径