生成带有附件的受保护 Django 网页的 PDF

Generate PDF of Protected Django Webpage with Attachments

所以我正在尝试生成我在 Django Web 应用程序中的视图的 PDF。此视图受保护,这意味着用户必须登录并具有特定权限才能查看该页面。我还有一些附件(在数据库中存储为 FileFields),我想附加到 PDF 的末尾。

我已经阅读了我能找到的关于如何使用 pdfkitreportlab 从网页生成 PDF 的大部分帖子,但由于某种原因,它们对我来说都失败了。

目前,我最接近的是使用 pdfkit 成功生成页面的 PDF,但这需要我删除要求用户登录并拥有页面权限的限制,这真的不是一个长期的选择。我找到了一些讨论在受保护页面上打印 pdf 并提供登录信息的帖子,但我无法使其中任何一个起作用。

我还没有找到任何关于如何包含附件的信息,也不知道从哪里开始。

如果需要,我非常乐意用更多信息或代码片段来更新这个问题,但这里有相当多的变化部分,我不想向人们提供无用的信息。让我知道是否需要提供任何其他信息,在此先感谢您的帮助。

您可以使用 pdfkit 来做到这一点。您可以使用 url 检索页面,pdfkit 将处理其余部分:

pdfkit.from_url('http://website.com/somepage', 'somepage.pdf')

您必须使用适当的 headers 正确访问该页面,因为它当然受到保护:

options = {
    'cookie': [
        ('cookie-name1', 'cookie-value1'),
        ('cookie-name2', 'cookie-value2'),
    ]
}

pdfkit.from_url('http://website.com/somepage', 'somepage.pdf')
`

如果您只是想保护它,您可以编写一个自定义身份验证后端,让您的服务器欺骗用户。太过分了,但它会解决你的问题,至少你可以了解自定义身份验证后端! (注意:您应该使用 HTTPS。)

https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#writing-an-authentication-backend

  1. app/auth_backends.py
  2. 中创建身份验证后端
  3. app.auth_backends.SpoofAuthBackend 后端添加到 settings.py,它采用 shared_secretuser_id
  4. 创建一条 URL 路线,例如 url(r'^spoof-user/(?P<user_id>\d+)/$', 'app.views.spoof_user', name="spoof-user")
  5. 添加视图 spoof_user,它必须同时调用 django.contrib.auth.authenticate(调用上面 #1 中的后端),并且在从 authenticate(...) 获取用户后,您用用户 django.contrib.auth.login(request, user)。最后,这个视图应该 return HttpResponseForbidden 如果共享秘密是错误的或者 HttpResponseRedirect 你真正想要的 PDF URL (在通过 [=22 以编程方式登录欺骗用户之后) =] 和 login).

您可能希望使用 cache.set('spoof-user-%s' % user_id, RANDOM_STRING, 30) 之类的东西为每个请求创建一个随机密钥,它可以将共享密钥保留 30 秒,以便有时间进行请求。然后执行pdf_response = requests.get("%s?shared_secret=1a2b3c&redirect_uri=/path/to/pdf/" % reverse('spoof-user', kwargs={'user_id': 1234}))。您的新视图将在身份验证后端测试提供的 shared_secret,登录用户请求并执行重定向到 request.GET.get('redirect_uri').

我成功了!通过结合使用 PyPDF2 和 pdfkit,我可以非常简单地使用它。它适用于受保护的页面,因为 django 负责将完整的 html 作为字符串获取,我只是将其传递给 pdfkit。它还支持附加附件,但我怀疑(虽然我还没有测试过)它是否适用于 pdf 以外的任何东西。

from django.template.loader import get_template
from PyPDF2 import PdfFileWriter, PdfFileReader
import pdfkit

def append_pdf(pdf, output):
    [output.addPage(pdf.getPage(page_num)) for page_num in range(pdf.numPages)]


def render_to_pdf():
    t = get_template('app/template.html')
    c = {'context_data': context_data}

    html = t.render(c)
    pdfkit.from_string(html, 'path/to/file.pdf')

    output = PdfFileWriter()
    append_pdf(PdfFileReader(open('path/to/file.pdf', "rb")), output)

    attaches = Attachment.objects.all()

    for attach in attaches:
        append_pdf(PdfFileReader(open(attach.file.path, "rb")), output)

    output.write(open('path/to/file_with_attachments.pdf', "wb"))