在Django中返回PDF响应

时间:2011-03-24 18:49:52

标签: python django pdf popen wkhtmltopdf

我问this one一个非常相似的问题。我正在Django的Ubuntu服务器上使用wkhtmltopdf创建一个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 = ...行上得到一个“'文件'对象不可调用”错误。

我也尝试将.communicate()添加到popen = ...行,但我似乎无法找到这样的pdf输出。我应该补充一点,在命令行输入command_args行可以很好地创建一个pdf。有人能指出我正确的方向吗?

5 个答案:

答案 0 :(得分:3)

wkhtmltopdf未输出Popen的PDF内容进行阅读。 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')

答案 1 :(得分: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。

答案 2 :(得分:1)

您获得'file' object is not callable的原因是因为一旦您拥有popen个对象,stdout就是文件句柄,而不是方法。不要打电话,只需使用它:

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

答案 3 :(得分:1)

我意识到这不使用wkhtmltopdf,但我觉得这更清洁了。

查看https://docs.djangoproject.com/en/dev/howto/outputting-pdf/

答案 4 :(得分:0)

您可能需要考虑更改

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

pdf_contents = subprocess.check_output(command_args.split())
response = ...

或旧版本:

process = Popen(command_args.split(), stdout=PIPE, stderr=PIPE)
pdf_contents = process.stdout.read()
response = ...

我建议你看一下check_output函数。

编辑:另外,不要调用terminate(),因为它会在不等待它完成的情况下终止进程,可能导致PDF损坏。您几乎只需要使用wait(),因为它将等待进程完成(因此输出所有它必须输出)。使用check_output()函数时,您不必担心它,因为它等待“默认”完成该过程。

除此之外,命名一个与模块同名的变量(我说的是tempfile)是一个的想法。我建议你把它改成tmpfile并查看NamedTemporaryFile,因为它比你现在正在做的更安全。