【问题标题】:How to serve a PDF file created by pdfkit.from_string() with Flask send_file()如何使用 Flask send_file() 提供由 pdfkit.from_string() 创建的 PDF 文件
【发布时间】:2020-04-15 02:59:03
【问题描述】:

我有一个 pdfkit PDF,可以作为 Sendgrid 附件正常工作,由以下函数创建:

def wish_lists_pdf(user=current_user):
    pdf_heading = "Thank you!"
    pdf_subheading = "Please find the Wish Lists you signed up to sponsor listed below."

    pdf_context = {
        'heading': pdf_heading,
        'subheading': pdf_subheading,
        'user': user,
    }
    css = os.path.join(basedir, 'static/main.css')
    pdf_content = render_template(
        'partials/email_lists_pdf.html', **pdf_context)

    path_wkhtmltopdf = app.config['WKHTMLTOPDF_EXE']
    config = pdfkit.configuration(wkhtmltopdf=path_wkhtmltopdf)

    pdf_file = pdfkit.from_string(
        pdf_content, False, configuration=config, css=css)

    bytes_file = BytesIO(pdf_file)

    return bytes_file

其实sendgrid需要这一行而不是字节编码:

    encoded_file = base64.b64encode(pdf_attachment).decode()

按照不同教程的建议,我使用这种编码和 b64 编码进行了尝试。我不太了解编码的目的,所以这可能是我的错误的一些原因。无论如何,这是我想要提供 PDF 文件的路线:

@bp.route('download_lists_pdf/<int:user_id>', methods=['GET'])
def download_lists_pdf(user_id):
    user = User.query.filter_by(id=user_id).first()
    pdf_file = wish_lists_pdf(user=user)
    return send_file(
        pdf_file,
        as_attachment=True,
        attachment_filename="Wish List Reminder Page.pdf",
        mimetype='application/pdf',
        )

这会下载一个完全空白的 0kb PDF 文件。有人可以帮助我了解如何以允许我从 pdfkit 提供此 PDF 的方式使用 send_file() 吗?同样,作为 Sendgrid 附件,该文件可以正常工作。

如果有帮助,这里是 sendgrid 附件配置...

    context = {
        'heading': heading,
        'subheading': subheading,
        'user': user,
    }

    message = Mail(
        from_email=app.config['ADMIN_EMAIL'],
        to_emails=app.config['EMAIL_RECIPIENTS'],
        subject=email_subject,
        html_content=render_template('partials/email_lists.html', **context),
    )

    encoded_file = base64.b64encode(pdf_attachment).decode()

    attached_file = Attachment(
        FileContent(encoded_file),
        FileName('Wish List Reminder Page.pdf'),
        FileType('application/pdf'),
        Disposition('attachment')
    )
    message.attachment = attached_file

    sg = SendGridAPIClient(app.config['SENDGRID_API_KEY'])
    response = sg.send(message)

提前感谢您的帮助。

编辑:在下面尝试但没有用

    bytes_file = BytesIO(pdf_file)

    return bytes(bytes_file), 200, {
        'Content-Type': 'application/pdf',
        'Content-Disposition': 'inline; filename="Wish List reminder sheet.pdf"'}

【问题讨论】:

    标签: flask file-transfer pdfkit


    【解决方案1】:

    如果您的 PDF 格式为 bytes_file

    return bytes(byte_file), 200, {
        'Content-Type': 'application/pdf',
        'Content-Disposition': 'inline; filename="nameofyourchoice.pdf"'}
    

    应该可以解决问题。

    【讨论】:

    • 您好,感谢您的回复。我试过了,它给了我这个错误:TypeError:视图函数没有返回有效响应。返回类型必须是字符串、字典、元组、响应实例或 WSGI 可调用,但它是 BytesIO。
    • 试试bytes(byte_file)。我从中提取的代码已经做到了。我修正了上面的答案。
    • 嗯,上面的编辑是你的意思吗?我试过了,得到了 TypeError: 'bytes' object cannot be compiled as integer
    【解决方案2】:

    我找到了解决方案。它使用直接从 pdfkit 返回的 PDF 文件,然后使用 Flask 响应来提供文件。

    这是返回 PDF 文件的函数:

    def wish_lists_pdf(user=current_user):
        pdf_heading = "Thank you!"
        pdf_subheading = "Please find the Wish Lists you signed up to sponsor listed below."
    
        pdf_context = {
            'heading': pdf_heading,
            'subheading': pdf_subheading,
            'user': user,
        }
        css = os.path.join(basedir, 'static/main.css')
        pdf_content = render_template(
            'partials/email_lists_pdf.html', **pdf_context)
    
        path_wkhtmltopdf = app.config['WKHTMLTOPDF_EXE']
        config = pdfkit.configuration(wkhtmltopdf=path_wkhtmltopdf)
    
        pdf_file = pdfkit.from_string(
            pdf_content, False, configuration=config, css=css)
    
        return pdf_file
    

    这是视图:

    @bp.route('download_lists_pdf/<int:user_id>', methods=['GET'])
    def download_lists_pdf(user_id):
        user = User.query.filter_by(id=user_id).first()
        pdf_file = wish_lists_pdf(user=user)
    
        response = Response(pdf_file)
        response.headers['Content-Disposition'] = "inline; 'Wish List reminder page'"
        response.mimetype = 'application/pdf'
        return response
    

    我认为如果它在新标签中打开,或者在没有在浏览器中打开的情况下下载它会更理想,但现在这已经足够了。

    【讨论】:

      【解决方案3】:

      如果您希望返回 PDF 文件作为响应,那么您可以试试这个-

      pdf = BytesIO(pdf_attachment)
      response = pdf.getvalue()
      pdf.close()
      return response, 200, {
              'Content-Type': 'application/pdf',
              'Content-Disposition': 'inline; filename="name_of_file.pdf"'} 
      

      【讨论】:

        【解决方案4】:

        我通过在我的模板目录中创建一个空白的 template.pdf 文件解决了这个问题,然后在 from_string 函数中这样做

        pdfkit.from_string(
                pdf_content, "templates/template.pdf", configuration=config, css=css
            )
        

        即将您的空白 template.pdf 的路径作为输出而不是 False 然后你会像这样调用send_file

        return send_file(
                'templates/template.pdf',
                mimetype="pdf",
                download_name="Wish List Reminder Page.pdf",
                as_attachment=True,
            )
        

        【讨论】:

          【解决方案5】:

          这些行对我很有用,可以重定向到带有 pdf 视图的新浏览器选项卡。谢谢!

          return bytes(byte_file), 200, {
           'Content-Type': 'application/pdf',
           'Content-Disposition': 'inline; filename="nameofyourchoice.pdf"'
          }
          

          【讨论】:

            猜你喜欢
            • 2012-03-30
            • 2017-09-24
            • 2012-07-14
            • 2015-10-19
            • 2020-05-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多