如何在wsgi中启动pdftk子进程?

8 投票
2 回答
2039 浏览
提问于 2025-04-17 03:01

我需要在Django中处理网页请求时启动一个pdftk进程,并等待它完成。现在我的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就无法创建输出文件,无论是在开发服务器还是mod_wsgi下都一样。这个讨论似乎表明,pdftk和操作系统信号之间可能有一些我不太理解的复杂问题。

有没有什么解决办法可以让这样的子进程调用在wsgi下正常工作?我不想用PyPDF来合并PDF文件,因为我需要合并的文件数量比较多(几百个),这样会导致内存不足(PyPDF在合并时需要把每个源PDF文件都保留在内存中)。

我是在最近的Ubuntu系统上,使用Python 2.6和2.7进行这个操作。

2 个回答

0

更新:在Python 3中使用Pdftk合并两个PDF文件:

这个问题已经被提出好几年了(2011年)。最开始提问的人说,在他们使用较旧版本的Python时,os.system没有起作用:

  • Python 2.6
  • Python 2.7

Python 3.4中,os.system对我来说是有效的:

  • import os
  • os.system("pdftk " + template_file + " fill_form " + data_file + " output " + export_file)

Python 3.5增加了subprocess.run这个功能:

  • subprocess.run("pdftk " + template_file + " fill_form " + data_file + " output " + export_file)

  • 我使用了文件的绝对路径:

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

库:

8

尝试使用绝对路径来指定输入和输出文件。因为在Apache下的当前工作目录可能和运行服务器的目录不一样,可能是任何地方。


这是在排除明显问题后的第二次尝试。

pdftk程序是一个Java程序,它需要能够生成或接收SIGPWR信号来触发垃圾回收或执行其他操作。问题在于,在Apache/mod_wsgi的守护进程模式下,请求处理线程中会阻止信号的接收,以确保只有主线程能接收到进程关闭的触发事件。当你为运行pdftk而分叉进程时,它不幸地继承了请求处理线程中的信号屏蔽。这就导致Java的垃圾回收过程受到影响,造成pdftk以奇怪的方式失败。

解决这个问题的唯一方法是使用Celery,让前端提交一个任务到Celery队列,然后由celeryd来分叉并执行pdftk。因为这个操作是在一个与Apache不同的进程中进行的,所以就不会遇到这个问题。

想了解更多详细信息,可以在Google上搜索mod_wsgi和pdftk,特别是在Google Groups中。

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

撰写回答