我对 Django 相当陌生,但我已经被一个问题困扰好几天了。我一直在网上寻找答案但没有成功。
我有一个名为 CreateReportView 的视图,我尝试使用 weasyprint 生成可下载的 pdf。将来我希望能够将字节数组保存到数据库中,以便用户可以查看生成的报告,然后下载它们。但第一步是下载生成的 pdf 文件。当使用 weasyprint write_pdf() 方法并保存到光盘时,例如到test.pdf,打开文件时pdf是可以的。但是,当我尝试将 PDF 生成为变量并将其作为附件保存在 File- 或 HTTPresponse 中时,我只能在浏览器中获得带有如下字符的文本:
%PDF-1.7 %🖤 5 0 obj <> stream xڽXm 6 _ ? 9 IpZ [(]h n ܧ p r d ή ױg yy ( G 1 @ \ ? z
我已经尝试了我能想到的一切,例如在 Http- 和 FileResponse 之间进行更改,将文件临时保存到光盘上,打开它然后将其传递给响应;使用缓冲区;等等..我也尝试过使用 Reportlab 库,但遇到了同样的问题。为了澄清,当我尝试使用 HttpResponse 时,我使用了以下代码:
response = HttpResponse(pdf_file, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="test.pdf"'
我看到有人使用缓冲区解决了问题(使用Weasyprint创建文件响应),但它对我不起作用。下面的视图中对此代码进行了注释。我也遇到同样的问题
我发现其他人通过更改“交通警察”例程解决了这个问题,但我似乎找不到需要更改的代码(Django - 无法下载文件(显示为文本))。
此外,在 Chrome 中使用检查模式时,我看到响应包含必要的标头,例如内容处置和内容类型。
任何帮助或指导将不胜感激。如果您需要更多信息,请告诉我。
提前谢谢您!
编辑:该网站还使用 Alpine.js 和 HTMX。模板中没有由 write_pdf 方法呈现的 HTMX 或 Alpine.js 脚本。在我的基本站点中,我有一些 HTMX 属性:
<body x-data="{'isModalOpen': false}" hx-boost='True' hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
查看文件:
from django.core.files import File
from django.db.models import Avg, Count, Case, When, IntegerField, DecimalField
from django.http import HttpResponse, FileResponse
from django.shortcuts import render
from django.template.loader import render_to_string
from django.views import View
from weasyprint import HTML
from salary.models import Salary
class CreateReportView(View):
def get(self, request, *args, ** kwargs):
... # Omitted unrelevant code
context = context={'results': results, 'results_per_gender': results_per_gender, 'results_per_region': results_per_region, 'results_titles': results_titles}
rendered_html = render_to_string('report_template.html', context=context)
# buffer = io.BytesIO()
# pdf_file = HTML(string=rendered_html)
# pdf_file.write_pdf(buffer)
pdf_file = HTML(string=rendered_html).write_pdf()
# buffer.seek(0)
response = FileResponse(pdf_file, as_attachment=True, filename="test.pdf")
return response
weasyprint.HTML.write_pdf()
返回一个bytes
实例,但当使用参数调用时,它返回None
。如果使用文件实例或文件的 str
路径作为 target
进行调用,则会将 PDF bytes
数据写入 target
。您一直在服务器上所做的事情没有任何问题。
buffer = BytesIO()
HTML(string=html).write_pdf(buffer)
buffer.seek(0)
return FileResponse(buffer, as_attachment=True, filename="test.pdf")
问题完全出在客户端。当您将 HTMX 属性
hx-boost="true"
添加到任何 HTML 标签时,如下所示:
<div hx-boost="true">
HTMX 的作用是增强正常锚点和
Form
标签以使用AJAX。这有一个很好的效果,如果用户没有启用 javascript,网站将继续工作。
GET 请求会将请求 URL 推送到浏览器历史记录堆栈上(如果来自锚标记)。 响应正文将用于交换页面
innerHTML
标签的body
。
如果响应正文是有效的 HTML,浏览器可以很好地呈现它。但既然不是,那就是问题所在。当使用 PDF 的
bytes
交换页面 innerHTML
标签的 body
时,浏览器无法渲染 PDF,因此得到的结果是:
%PDF-1.7 %🖤 5 0 obj <> stream xڽXm 6 _ ? 9 IpZ [(]h n ܧ p r d ή ױg yy ( G 1 @ \ ? z
要完成响应并让浏览器自行处理下载,而不尝试将页面
innerHTML
标记的 body
与响应正文交换,请将 hx-swap="none"
属性添加到涉及发出请求的部分:
<div hx-boost="true" hx-swap="none">
我没有在页面上提及我正在使用 HTMX。在我的基本页面中,我使用
hx-boost=true
(htmx) 作为我的 body 标记中的属性。删除该属性时,它似乎按预期工作。我现在意识到我应该提供这些额外的细节,但我认为它们在这种情况下并不重要。感谢您的帮助。