是否可以使用 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】:

我遇到了类似的情况,我能够“生成和流式下载” csvjsonxml 类型的文件,我想对 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?

是否可以使用 HTML/Javascript 按名称启动应用程序?

是否可以使用 jQuery 读取元标记

是否可以使用批处理文件检查两个文件是否共享相同的名称?

是否可以使用 INFORMATION_SCHEMA 模式来检查数据库是否存在?