在Django中返回PDF响应

3 投票
4 回答
11399 浏览
提问于 2025-04-16 14:21

我在问一个和这个问题非常相似的问题。我正在使用wkhtmltopdf在Ubuntu服务器上通过Django创建一个PDF。

from tempfile import *
from subprocess import Popen, PIPE

tempfile = gettempdir()+"/results.pdf"
papersize = 'Tabloid'
orientation = 'Landscape'
command_args = "wkhtmltopdf -O %s -s %s -T 0 -R 0 -B 0 -L 0 http://pdfurl %s" %(orientation, papersize, tempfile)
popen = Popen(command_args, stdout=PIPE, stderr=PIPE)
pdf_contents = popen.stdout().read()
popen.terminate()
popen.wait()
response = HttpResponse(pdf_contents, mimetype='application/pdf')
return response

在popen = Popen...这一行,我遇到了“没有这样的文件或目录”的错误。所以我把这一行改成了

popen = Popen(["sh", "-c", command_args], stdout=PIPE, stderr=PIPE)

现在在pdf_contents =...这一行,我又遇到了“'file'对象不可调用”的错误。

我还尝试在popen =...这一行添加了.communicate(),但我似乎找不到PDF的输出。我应该补充一下,直接在命令行输入command_args这一行可以正常生成PDF。有人能给我指个方向吗?

4 个回答

1

你看到的错误信息 'file' object is not callable 是因为当你创建了一个 popen 对象后,stdout 其实是一个文件句柄,而不是一个可以调用的方法。你不需要去调用它,只需要直接使用它就可以了:

popen = Popen(command_args, stdout=PIPE, stderr=PIPE)
pdf_contents = popen.stdout.read()
3

wkhtmltopdf 没有把 PDF 的内容输出给 Popen 来读取。pdf_contents 里正确地包含了命令的输出(什么都没有)。如果你想把内容返回给客户端,你需要读取输出文件的内容(见下文),或者跳过输出文件,让 wkhtmltopdf 直接输出 PDF 的内容。

from tempfile import *
from subprocess import Popen, PIPE

tempfile = gettempdir()+"/results.pdf"
command_args = "/path/to/wkhtmltopdf -O %s -s %s -T 0 -R 0 -B 0 -L 0 http://pdfurl %s" % ('Landscape', 'Tabloid', tempfile)
popen = Popen(["sh", "-c", command_args])
popen.wait()
f = open(tempfile, 'r')
pdf_contents = f.read()
f.close()

return HttpResponse(pdf_contents, mimetype='application/pdf')
2

你的第一个版本失败了,因为Python不知道wkhtmltopdf在哪里。Python不会自动去查找这个路径。你的第二个版本则是把命令交给了一个shell(命令行),它会处理这个问题。你也可以通过添加一个参数shell=True来达到同样的效果。

第二个问题(正如其他人提到的)是你在不该调用stdout()的时候调用了它。

第三个问题是你的wkhtmltopdf命令写错了。你现在是这样做的:

wkhtmltopdf -O %s -s %s -T 0 -R 0 -B 0 -L 0 http://pdfurl tempfile/results.pdf

其实你应该这样传递:

wkhtmltopdf -O %s -s %s -T 0 -R 0 -B 0 -L 0 http://pdfurl -

这样一来,wkhtmltopdf就会把输出写到标准输出,你就可以读取它。如果你再传递一个-作为源,你就可以通过标准输入发送html内容。

撰写回答