开发者

Returning a PDF response in Django

I'm asking a very similar question to this one. I'm creating a pdf using wkhtmltopdf on an Ubuntu server in Django.

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

This gives me a "no such file or directory" error on the popen = Popen... line. So I changed that line to

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

and now I get a "'file' object is not callable" error on the pdf_contents =... line.

I've also tried adding .communicate() to the popen =... line but I can't seem to l开发者_Go百科ocate the pdf output that way. I should add that typing the command_args line into the command line creates a pdf just fine. Can anyone point me in the right direction?


wkhtmltopdf is not outputting the contents of the PDF for Popen to read it. pdf_contents correctly contains the output of the command (nothing). You will need to read the contents of the output file if you want to return it to the client (see below), or skip the output file and make wkhtmltopdf output the contents of the pdf directly,

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')


Your first version fails because python does not know where wkhtmltopdf is located. Python will not check your path for that. Your second version passes the command to a shell which takes care of that. You achieve the same effect by passing a shell=True argument.

The second problem (as others have noted) is that you call stdout() when you shouldn't.

The third problem is that your wkhtmltopdf command is wrong. You are doing:

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

Instead you should pass

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

That way wkhtmltopdf will write the output to standard output and you can read it. If you pass another - as the source, you can send the html over the standard input.


The reason you're getting 'file' object is not callable is because once you have your popen object, stdout is a filehandle, not a method. Don't call it, just use it:

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


You might want to consider changing

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

to

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

or in older versions:

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

I suggest you take a look at the check_output function.

EDIT: Also, don't call terminate(), as it will kill the process without waiting it to complete, possibly resulting in a corrupted PDF. You will pretty much only need to use wait(), as it will wait for the process to complete (and thus output all it has to output). When using the check_output() function, you need not to worry about it, as it waits for the process to complete by "default".

Other than that, naming a variable with the same name as a module (I'm talking about tempfile) is a bad idea. I suggest you to change it to tmpfile and to check out NamedTemporaryFiles as it is safer to use than what you are doing right now.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜