使用StreamingHttpResponse生成PDF是否像使用CSV处理大型数据集一样可行?

14

我有一个庞大的数据集需要生成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吗?https://docs.djangoproject.com/en/3.1/ref/request-response/#fileresponse-objects - trinchet
想一下。CSV可以有多个表格页,如果需要的话可以进行编辑。而PDF则不能修改。PDF是一个大型文件,没有任何表格页。 - Siva Sankar
4个回答

3
把CSV比作水果沙拉。你可以在一个大碗里切香蕉,加些西柚、菠萝……然后将整个混合物分成单独的份量,端到餐桌上(也就是说,生成CSV文件,然后将其发送给客户端)。但你也可以直接制作单独的份量:在一个小碗中切几片香蕉,加些西柚、菠萝……将这个小碗端到餐桌上,为其他单独的份量重复这个过程(也就是说,在生成时将CSV文件分块逐部分发送给客户端)。
嗯,如果CSV是水果沙拉,那么PDF就是蛋糕。你必须混合所有的材料并把它放进烤箱里。这意味着你不能在整个蛋糕烤好之前就把一块蛋糕端到餐桌上。同样地,你不能在PDF文件完全生成之前开始向客户端发送文件。
因此,回答你的问题,(response = StreamingHttpResponse((writer.writerow(row) for row in rows), content_type="text/csv"))对于PDF来说是不可行的。
然而,一旦您的文件生成完成,您可以使用其他答案中提到的FileResponse将其流式传输到客户端。
如果您的问题是生成PDF太耗时(可能会触发超时错误),则需要考虑以下几点:
  1. 尝试优化生成算法的速度
  2. 在客户端请求之前在后台生成文件并将其存储在您的存储系统中。您可能需要使用cronjob或celery来触发PDF的生成,而不会阻止HTTP请求。
  3. 使用Websockets在文件准备好下载时立即将文件发送给客户端(请参见django-channels

2

您尝试过FileResponse吗?

类似这样的东西应该可以工作,基本上可以在Django文档中找到:

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')

我尝试使用这个代码片段生成一个大的PDF,例如对于.drawString(...)方法进行了100k次迭代,但在执行p.save()之前下载没有开始。 - JPG
是的,在流式传输开始之前必须创建PDF。 - WombatPM

0
我曾经遇到过类似的情况,我能够生成和流式下载csv、json和xml类型的文件,我想用同样的方法处理Excel - xlsx文件。
不幸的是,我无法做到这一点。但是,在那段时间里,我发现了一些事情。
  1. 文件,CSV,JSON和XML是具有适当表示的文本文件。但是,对于PDF或Excel(或类似文件),这些文件是使用适当的格式和元数据构建的。

  2. PDF和类似文档的二进制数据仅在调用某些特定方法时才写入io缓冲区。[reportlab的showPage()和save()方法。(来源- Django Doc)]

  3. 如果我们检查文件流,PDF和Excel需要复杂的特殊应用程序(例如:PDF阅读器、浏览器等)才能查看/读取数据,而对于CSV和JSON,我们只需要一个简单的文本编辑器。

所以,我得出结论,“在流下载中动态生成文件”的过程(不确定应该使用什么正确的技术术语)并非所有文件类型都可以实现,而只有一些面向文本的文件才能实现。 注意:这是我的有限经验,可能是错误的。

0

从您提供的链接来看,它提供了一个关于如何使用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格式化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接