如何在Dash应用中内存生成并下载PDF文件
问题:Dash应用中实现内存生成PDF并直接供用户下载
我开发了一个Dash应用,希望支持用户下载PDF报告。目前通过pdfkit和jinja实现了本地功能,但需要将文件保存到服务器后再供下载,用户可能对此有顾虑,因此希望仅在内存中处理临时文件(客户端侧)。以下是我的代码,请问有没有更优实现方式?
from dash import Dash, dcc, html, Input, Output import pdfkit import jinja2 # Set up sample pdf with pdfkit and jinja based on this tutorial: https://www.youtube.com/watch?v=1IYtkkEOuoU my_name = "Frank " item1 = "TV" today_date = "2022" context = {'my_name': my_name, 'item1': item1, 'today_date': today_date} template_loader = jinja2.FileSystemLoader('./') template_env = jinja2.Environment(loader=template_loader) html_template = 'my-basic-template.html' template = template_env.get_template(html_template) output = template.render(context) # downloaded wkhtmltopdf locally: path_wkhtmltopdf = r'C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe' config = pdfkit.configuration(wkhtmltopdf=path_wkhtmltopdf) output_pdf = 'pdf_generated.pdf' pdfkit.from_string(output, output_pdf, configuration=config) # Make Dash app app = Dash(__name__) app.layout = html.Div([ html.Button("Download pdf", id="btn-download-pdf"), dcc.Download(id="download-pdf") ]) @app.callback( Output("download-pdf", "data"), Input("btn-download-pdf", "n_clicks"), prevent_initial_call=True, ) def func(n_clicks): pdf = pdfkit.from_string(output, configuration=config) print(output) return dcc.send_file("pdf_generated.pdf", "test_pdf.pdf") if __name__ == '__main__': app.run_server(debug=True)
优化实现方案
核心思路是完全在内存中生成PDF字节流,直接传给客户端下载,避免服务器写入文件,具体优化如下:
优化后的代码
from dash import Dash, dcc, html, Input, Output import pdfkit import jinja2 # 初始化wkhtmltopdf配置(全局只执行一次) path_wkhtmltopdf = r'C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe' config = pdfkit.configuration(wkhtmltopdf=path_wkhtmltopdf) # 初始化Jinja2模板环境(全局只执行一次) template_env = jinja2.Environment(loader=jinja2.FileSystemLoader('./')) # 创建Dash应用 app = Dash(__name__) app.layout = html.Div([ html.Button("Download pdf", id="btn-download-pdf"), dcc.Download(id="download-pdf") ]) @app.callback( Output("download-pdf", "data"), Input("btn-download-pdf", "n_clicks"), prevent_initial_call=True, ) def generate_and_download_pdf(n_clicks): # 动态生成上下文数据(可替换为用户输入/数据库查询等动态内容) context = { 'my_name': "Frank", 'item1': "TV", 'today_date': "2022" } # 渲染HTML模板 template = template_env.get_template('my-basic-template.html') html_content = template.render(context) # 在内存中生成PDF字节流(第二个参数传False,不生成本地文件) pdf_bytes = pdfkit.from_string(html_content, False, configuration=config) # 直接返回二进制数据供客户端下载 return dcc.send_bytes(pdf_bytes, filename="test_pdf.pdf") if __name__ == '__main__': app.run_server(debug=True)
关键优化点
- 内存生成PDF:调用
pdfkit.from_string时第二个参数传False,方法会直接返回PDF的二进制字节流,完全跳过服务器文件写入步骤。 - 使用
dcc.send_bytes:Dash的send_bytes组件支持直接发送二进制数据,无需依赖服务器本地文件,实现纯内存流转。 - 逻辑模块化:将模板渲染、PDF生成逻辑封装到回调函数内,方便后续扩展为动态数据(比如根据用户输入生成个性化PDF)。
- 清理冗余代码:移除全局范围内生成本地PDF文件的代码,减少不必要的IO操作。
内容的提问来源于stack exchange,提问作者NRVA
相关产品推荐
相关产品推荐

