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.
精彩评论