一种比使用 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
以及生成器函数,将报告生成到临时文件中,而不是返回到浏览器。但当你这样做的时候,yield
HTML 片段返回浏览器,让用户了解进度,例如:
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