是否可以使用 StreamingHttpResponse 生成 PDF,因为可以使用 CSV 来生成大型数据集?
Posted
技术标签:
【中文标题】是否可以使用 StreamingHttpResponse 生成 PDF,因为可以使用 CSV 来生成大型数据集?【英文标题】:Is it possible to generate PDF with StreamingHttpResponse as it's possible to do so with CSV for large dataset? 【发布时间】:2020-11-30 03:59:36 【问题描述】:我有一个大型数据集,我必须为其生成 CSV 和 PDF。对于 CSV,我使用此指南:https://docs.djangoproject.com/en/3.1/howto/outputting-csv/
import csv
from django.http import StreamingHttpResponse
class Echo:
"""An object that implements just the write method of the file-like
interface.
"""
def write(self, value):
"""Write the value by returning it, instead of storing in a buffer."""
return value
def some_streaming_csv_view(request):
"""A view that streams a large CSV file."""
# Generate a sequence of rows. The range is based on the maximum number of
# rows that can be handled by a single sheet in most spreadsheet
# applications.
rows = (["Row ".format(idx), str(idx)] for idx in range(65536))
pseudo_buffer = Echo()
writer = csv.writer(pseudo_buffer)
response = StreamingHttpResponse((writer.writerow(row) for row in rows),
content_type="text/csv")
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
return response
效果很好。但是,我找不到可以为 PDF 做的任何事情。它可以?我使用render_to_pdf
以及PDF 模板。
【问题讨论】:
您尝试过 FileResponse 吗? docs.djangoproject.com/en/3.1/ref/request-response/… 这样想。如果需要,CSV 可以有多个工作表。它是可编辑的。 PDF不能修改的地方。 PDF 是一个大文档,没有任何工作表。 【参考方案1】:我遇到了类似的情况,我能够“生成和流式下载” csv
、json
和 xml
类型的文件,我想对 Excel - xlsx
文件。
很遗憾,我做不到。但是,在那段时间我发现了一些东西
文件、CSV、JSON 和 XML 是 具有适当表示的文本文件。但是,对于 PDF 或 Excel(或类似文件),这些文件是使用正确的格式和正确的元数据构建的。
PDF和类似文档的二进制数据只有在我们调用某些特定方法时才会写入io缓冲区。 [showPage()
和save()
的reportlab
方法。 (来源-Django Doc)]
如果我们检查文件流,PDF 和 Excel 需要复杂的特殊应用程序(例如:PDF 阅读器、Bowsers 等) 来查看/读取数据,而使用 CSV和 JSON,我们只需要一个简单的文本编辑器。
因此,我得出结论,“通过流式下载动态生成文件”的过程(不确定我应该使用什么正确的技术术语)并非适用于所有文件类型,但仅适用于少数面向文本的文件
注意:这是我有限的经验,可能是错误的。
【讨论】:
【参考方案2】:你试过FileResponse吗?
这样的东西应该可以,基本上你可以在Django doc找到:
import io
from django.http import FileResponse
from reportlab.pdfgen import canvas
def stream_pdf(request):
buffer = io.BytesIO()
p = canvas.Canvas(buffer)
p.drawString(10, 10, "Hello world.")
p.showPage()
p.save()
buffer.seek(io.SEEK_SET)
return FileResponse(buffer, as_attachment=True, filename='helloworld.pdf')
【讨论】:
我试过这个 sn-p 来生成一个大的 PDF,比如.drawString(...)
方法的 100k 迭代,并且在 p.save()
执行之前没有开始下载
是的,必须先创建 PDF,然后才能开始流式传输。【参考方案3】:
查看您提供的链接,它确实提供了指向使用reportlab 动态创建和发送 pdf 文件的页面的链接。
import io
from django.http import FileResponse
from reportlab.pdfgen import canvas
def some_view(request):
# Create a file-like buffer to receive PDF data.
buffer = io.BytesIO()
# Create the PDF object, using the buffer as its "file."
p = canvas.Canvas(buffer)
# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
p.drawString(100, 100, "Hello world.")
# Close the PDF object cleanly, and we're done.
p.showPage()
p.save()
# FileResponse sets the Content-Disposition header so that browsers
# present the option to save the file.
buffer.seek(0)
return FileResponse(buffer, as_attachment=True, filename='hello.pdf')
这是reportlab api 文档的链接。它有点冗长,并且存储在令人讨厌的单页 pdf 中,但它应该可以让您启动并运行,并且能够根据需要很好地格式化 PDF。
【讨论】:
【参考方案4】:将 CSV 视为水果沙拉。您可以在一个大锅中切片香蕉,加入一些葡萄柚、一些菠萝……然后将整个香蕉分成单独的部分,然后放在桌子上(这是:您生成 CSV 文件,然后将其发送到客户端)。但是你也可以直接做单独的部分:在一个小碗里切一些香蕉片,加入一些葡萄柚,一些菠萝,......把这个小碗带到桌子上,然后对其他单独的部分重复这个过程(这是:您生成 CSV 文件并在生成时将其部分发送给客户端)。
如果 CSV 是水果沙拉,那么 PDF 就是蛋糕。您必须混合所有成分并将其放入烤箱中。这意味着在烤完整个蛋糕之前,您不能将一片蛋糕带到餐桌上。同样,在 PDF 文件完全生成之前,您不能开始将其发送给客户端。
所以,为了回答您的问题,这个 (response = StreamingHttpResponse((writer.writerow(row) for row in rows), content_type="text/csv")
) 不能用于 PDF。
但是,生成文件后,您可以使用 FileResponse
将其流式传输到客户端,如其他答案中所述。
如果您的问题是 PDF 的生成时间过长(例如,可能会触发超时错误),请考虑以下几点:
-
尝试优化生成算法的速度
在客户端请求之前在后台生成文件并将其存储在您的存储系统中。您可能希望使用 cronjob 或 celery 来触发 PDF 的生成,而不会阻止 HTTP 请求。
一旦准备好下载文件,就使用 websockets 将文件发送到客户端(请参阅django-channels)
【讨论】:
以上是关于是否可以使用 StreamingHttpResponse 生成 PDF,因为可以使用 CSV 来生成大型数据集?的主要内容,如果未能解决你的问题,请参考以下文章
是否可以使用 JavaScript 确定 GeoJSON 点是否在 GeoJSON 多边形内?
是否可以检查是否使用modernizr 启用了cookie?