如何在wsgi中启动pdftk子进程?
我需要在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 个回答
更新:在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.')
库:
尝试使用绝对路径来指定输入和输出文件。因为在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