Как запустить подпроцесс pdftk в wsgi?

Мне нужно запустить процесс pdftk при обслуживании веб-запроса в Django и дождаться его завершения. Мой текущий код pdftk выглядит так:

proc = subprocess.Popen(["/usr/bin/pdftk", 
                         "/tmp/infile1.pdf", 
                         "/tmp/infile2.pdf", 
                         "cat", "output", "/tmp/outfile.pdf"])    
proc.communicate()

Это отлично работает, пока я работаю на сервере разработки (под пользователем www-data). Но как только я переключаюсь на mod_wsgi, ничего не меняя, код зависает на proc.communicate(), а "outfile.pdf" остается как дескриптор открытого файла нулевой длины.

Я пробовал несколько вариантов вызова подпроцесса (а также простой старый os.system) - установка stdin / stdout / stderr на PIPE или на различные файловые дескрипторы ничего не меняет. Использование "shell = True" предотвращает зависание proc.communicate(), но тогда pdftk не может создать выходной файл, как на devserver, так и на mod_wsgi. Это обсуждение, похоже, указывает на то, что может быть более глубокое вуду происходит с сигналами ОС и pdftk, которых я не понимаю.

Есть ли какие-либо обходные пути, чтобы заставить такой вызов подпроцесса правильно работать под wsgi? Я избегаю использования PyPDF для объединения файлов PDF, потому что мне нужно объединить достаточно большое количество файлов (несколько сотен), чтобы не хватило памяти (PyPDF должен держать каждый исходный файл PDF открытым в памяти при их объединении).

Я делаю это в последней версии Ubuntu, pythons 2.6 и 2.7.


person user85461    schedule 25.09.2011    source источник


Ответы (2)


Попробуйте указать абсолютные пути файловой системы к входным и выходным файлам. Текущий рабочий каталог в Apache не будет тем же каталогом, что и сервер запуска, и может быть любым.


Вторая попытка после устранения очевидного.

Программа pdftk - это программа на Java, которая полагается на возможность генерировать / получать сигнал SIGPWR для запуска сборки мусора или выполнения других действий. Проблема в том, что в Apache / mod_wsgi в режиме демона сигналы блокируются в потоках обработчика запросов, чтобы гарантировать, что они будут получены только основным потоком, который ищет триггерные события завершения процесса. Когда вы запускаете процесс для запуска pdftk, он, к сожалению, наследует заблокированную сигмаску от потока обработчика запросов. Следствием этого является то, что он препятствует работе процесса сборки мусора Java и вызывает странные отказы pdftk.

Единственное решение для этого - использовать Celery и заставить внешний интерфейс отправить задание в очередь Celery для celeryd, чтобы затем выполнить вилку и выполнить pdftk. Поскольку это делается в процессе, созданном отдельно от Apache, у вас не будет этой проблемы.

Для получения более подробной информации Google для mod_wsgi и pdftk, в частности, в группах Google.

http://groups.google.com/group/modwsgi/search?group=modwsgi&q=pdftk&qt_g=Search+this+group

person Graham Dumpleton    schedule 25.09.2011
comment
На самом деле я использую абсолютные пути, спасибо. Я обновил пример кода, чтобы отразить это. К сожалению, проблема остается. - person user85461; 25.09.2011
comment
Спасибо, разветвление сельдерея сработало. Я выполнил задачу с сельдереем синхронно (используя task.delay().get(), чтобы это могло произойти в течение одного цикла ответа, что дает мне желаемый результат. - person user85461; 25.09.2011
comment
Ничего себе, я не смог найти это решение где-либо еще, но это именно то, что мне нужно было сделать, чтобы PDFTK заработал. СПАСИБО! - person Furbeenator; 16.07.2013

Обновление: объединение двух PDF-файлов вместе с использованием Pdftk на Python 3:

С момента публикации этого вопроса прошло несколько лет. (2011). В исходном плакате говорилось, что os.system не работает для них, когда они запускают более старые версии python:

  • Python 2.6 и
  • Python 2.7

В Python 3.4 os.system работал у меня:

  • импорт ОС
  • os.system ("pdftk" + файл_шаблона + "fill_form" + файл_данных + "output" + файл_экспорта)

Python 3.5 добавляет subprocess.run.

  • subprocess.run ("pdftk" + файл_шаблона + "fill_form" + файл_данных + "output" + файл_экспорта)

  • Я использовал абсолютные пути к своим файлам:

    • template_file = "/var/www/myproject/static/"

Я запустил это с Django 1.10, и результат был сохранен в export_file.

Как объединить два PDF-файла и отобразить PDF-файл:

from django.http import HttpResponse, HttpResponseNotFound
from django.core.files.storage import FileSystemStorage
from fdfgen import forge_fdf
import os

template_file = = "/var/www/myproject/template.pdf"
data_file = "/var/www/myproject/data.fdf"
export_file ="/var/www/myproject/pdf_output.pdf"

fields = {}
fields['organization_name'] = organization_name
fields['address_line_1'] = address_line_1
fields['request_date'] = request_date
fields['amount'] = amount
field_list = [(field, fields[field]) for field in fields]

fdf = forge_fdf("",field_list,[],[],[])
fdf_file = open(data_file,"wb")
fdf_file.write(fdf)
fdf_file.close()

os.system("pdftk " + template_file + " fill_form " + data_file + " output " + export_file)
time.sleep(1)

fs = FileSystemStorage()
if fs.exists(export_file):
  with fs.open(export_file) as pdf:
    return HttpResponse(pdf, content_type='application/pdf; charset=utf-8')
else:
    return HttpResponseNotFound('The requested pdf was not found in our server.')

Библиотеки:

person Tim Langeman    schedule 24.03.2017