在 Django 中延迟文件下载的正确方法

2024-05-02

我有一个基于类的视图,它触发用户编写和下载报告。

通常在def get我刚刚编译的班级报告,添加response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'并向用户返回响应。

问题是有些报道large当他们编译时,请求超时发生。

我知道right处理这个问题的方法是将其委托给后台进程(例如Celery http://www.celeryproject.org/)。但问题是,这意味着我必须将这些报告存储在某处,并编写一个定期清理报告目录的 cronjob,而不是创建一个在用户下载报告时就不再存在的临时文件。

Django 有没有更优雅的方式来处理这个问题?


一种比使用 celery 不那么花哨的解决方案是使用 Django 的StreamingHttpResponse:

(https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.StreamingHttpResponse https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.StreamingHttpResponse

这样,您可以使用生成器函数,它是一个使用yield以迭代器的形式返回其结果。这允许您在生成数据时返回数据,而不是在完成后立即返回所有数据。你可以yield在报告的每一行或每一部分之后..从而保持数据流返回浏览器。

但是......这仅在您一点一点地构建完成的文件时才有效......例如,CSV 文件。如果您要返回需要一次性格式化的内容,例如,如果您使用类似wkhtmltopdf完成后要生成pdf文件,那就没那么容易了。

但还是有一个解决办法:

在这种情况下你可以做的是,使用StreamingHttpReponse以及生成器函数,将报告生成到临时文件中,而不是返回到浏览器。但当你这样做的时候,yieldHTML 片段返回浏览器,让用户了解进度,例如:

def get(self, request, **kwargs):

    # first you need a tempfile name.. do that however you like
    tempfile = "kfjkdsjfksjfks"
    # then you need to create a view which will open that file and serve it
    # but I won't show that here.  
    # For security reasons it has to serve only out of one directory 
    # that is dedicated to this.
    fetchurl = reverse('reportgetter_url') + '?file=' + tempfile

    def reportgen():
        yield 'Starting report generation..<br>'
        # do some stuff to generate your report into the tempfile
        yield 'Doing this..<br>'
        # do this
        yield 'Doing that..<br>'
        # do that
        yield 'Finished.<br>'
        # when the browser receives this script, it'll go to fetchurl where
        # you will send them the finished report.
        yield '<script>document.location="%s";</script>' % fetchurl

    return http.StreamingHttpResponse(reportgen())

显然这不是一个完整的示例,但应该可以给您带来想法。

当您的用户获取此视图时,他们将看到报告的进度。最后,您发送 javacript,它将浏览器重定向到您必须编写的另一个视图,该视图返回包含完成文件的响应。当浏览器获取此 javacript 时,如果返回临时文件的视图在返回之前将响应 Content-Disposition 设置为附件,例如:

response['Content-Disposition'] = 'attachment; filename="%s"' % filename

..然后浏览器将停留在当前页面上显示您的进度..并且只需为用户弹出一个文件保存对话框。

对于清理工作,无论如何,您都需要一个 cron 作业……因为如果人们不等待,他们将永远不会收到报告。有时事情并不顺利...所以你可以只清理早于 1 小时的文件。对于很多系统来说这是可以接受的。

但是如果你想立即清理,如果你使用的是unix/linux,你可以做的是使用一个旧的unix文件系统技巧:在打开时被删除的文件在关闭之前不会真正消失。所以,打开你的临时文件..然后删除它。然后返回您的回复。响应发送完成后,文件使用的空间将被释放。

PS:我应该补充一下..如果您采用第二种方法,您可以使用一个视图来完成这两项工作..只是:

if `file` in request.GET:
    # file= was in the url.. they are trying to get an already generated report
    with open(thepathname) as f:
        os.unlink(f)
        # file has been 'deleted' but f is still a valid open file
        response = HttpResponse( etc etc etc)
        response['Content-Disposition'] = 'attachment; filename="thereport"'
        response.write(f)
        return response
else:
   # generate the report
   # as above
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Django 中延迟文件下载的正确方法 的相关文章

随机推荐

  • 如何在 mongodb + C# 中计数、选择和更新嵌套元素

    我正在创建一个开源社交网络 我不知道是否可以在此处发布 URL 使用 net core 2 1 mongo atlas 使用驱动程序 v2 9 2 我正在从 MS SQL 我知道如何对其进行操作 迁移到 mongo 我仍在学习 我有两个实体
  • IE 11 使用 HTML input=file 标签时锁定文件

    我在 IE11 中使用文件输入中的浏览来选择文件 我在资源管理器中使用shift delete删除了该文件 然后 当我刷新文件夹时 我删除的文件会再次出现在资源管理器中 无论如何 我可以通过客户端 JavaScript 释放文件句柄吗 我在
  • 在 Haskell/Yampa 和 HOOD 中调试游戏对象的输出

    我一直坚持使用 Haskell Yampa Arrows with HOOD 为我的游戏对象生成调试输出 我的引擎基本上运行一系列游戏对象 这些对象产生输出状态 线 圆 然后进行渲染 data Output Circle Position2
  • 动态添加导航抽屉中的项目

    我创建了抽屉 但是我想动态设置抽屉的项目列表 意味着从数据库获取数据并设置为抽屉列表 是否可以 是的 比如何 我也知道静态抽屉 尝试这个 final Menu menu navigationView getMenu for int i 1
  • .Net HashSet唯一性计算完全基于哈希码吗?

    我想知道 Net 是否HashSet
  • xmlstarlet:通过位置号选择元素

    我有这个 XML 文件
  • 如何在
    CSS 中正确定位箭头

    我想更改详细信息中出现的箭头的位置 我尝试过 float left 但如果线条太大 如上面示例中的线条 则当我调整窗口大小时 箭头将移动到下方线条的开头 我希望它保留在第一行的第一个字母之外 我怎样才能做到这一点 Example
  • WebAPI如何处理嵌套资源?

    我正在寻找有关使用 WebAPI 2 时最佳实践的意见和建议 假设我有两个控制器 用户和书籍 并且想要接受这些路由 users user id books lt books owned by user id books lt all boo
  • 在 Windows 上使用 Python 打开设备句柄

    我正在尝试使用 Giveio sys 驱动程序 该驱动程序需要先打开一个 文件 然后才能访问受保护的内存 我正在查看 WinAVR AVRdude 中的 C 示例 它使用以下语法 define DRIVERNAME giveio HANDL
  • Java-Android 上的 MulticastSocket 问题

    我开始使用 MulticastSocket 进行编码 尝试制作一个带有客户端和服务器的简单应用程序来发送消息 我的服务器代码 import java io IOException import java net DatagramPacket
  • 具有多个 FROM 表的 TableGateway

    我想做一个简单的INNER JOINZend2 中的两个表之间 具体来说 我想在 Zend2 中这样做 SELECT FROM foo bar WHERE foo foreign id bar id 我有一个FooTable class F
  • 尝试使用 C#.Net 编码 HMAC-SHA256

    我尝试将 HMAC SHA256 算法编码为函数 HMAC K m H K opad H K ipad m where H 是加密哈希函数 K 是一个秘密密钥 在哈希函数的输入块大小或原始密钥的哈希值 如果它长于该块大小 的右侧添加了额外的
  • font-face 声明中还需要 eot、ttf 和 svg 吗?

    到目前为止 我一直使用 Paul Irish 的防弹字体语法 http www paulirish com 2009 bulletproof font face implementation syntax 但我只是在寻找对 woff 和 w
  • 将表中的行相对于另一个表拖放[关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 嘿 我有两个行数相同的表 例如 如果我尝试对 tableOne 中的一行 例如 row 3 进行排序 则其他表 tabl
  • printf 字符串,可变长度项

    define SIZE 9 int number 5 char letters SIZE this wont be null terminated char fmt string 20 sprintf fmt string d ds SIZ
  • scala-js 如何与 sbt-web 集成?

    我想用scala js https github com scala js scala js with sbt web https github com sbt sbt web以这样的方式 它可以被编译以生成添加到资产管道中的 JavaSc
  • 如何用 PHP 编写固定宽度的文本文件

    我正在尝试做一些应该很容易的事情 但遇到了问题 我想要做的就是从 MySQL 表生成报告 并将其作为纯 txt 文件 我需要文件固定宽度 因此一切都排列整齐并且看起来不错 我将使用 courier 字体 它只是一个准系统表格 但我如何才能真
  • 当应用程序崩溃时,Android Studio ADB 会清除 logcat 文件!哦美伊

    刚刚升级了最新的Android Studio 现在 当应用程序崩溃时 日志文件 adb logcat 就会消失 它不仅停止记录整个日志似乎突然被删除了 对于调试来说不太酷 我需要设置日志缓存大小或其他什么吗 还注意到调试器需要很长时间来收集
  • DBI:在 eval 中引发错误

    这个问题参考了池上的评论 But if you re going to put an eval around every statement just use RaiseError gt 0 in this thread https sta
  • 在 Django 中延迟文件下载的正确方法

    我有一个基于类的视图 它触发用户编写和下载报告 通常在def get我刚刚编译的班级报告 添加response Content Disposition attachment filename somefilename pdf 并向用户返回响