[Django]-Returning a PDF response in Django

2đź‘Ť

âś…

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.

👤Winston Ewert

3đź‘Ť

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')
👤Jack M.

1đź‘Ť

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()
👤Jack M.

0đź‘Ť

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.

👤brahle

Leave a comment