【问题标题】:How to launch a pdftk subprocess while in wsgi?如何在 wsgi 中启动 pdftk 子进程?
【发布时间】:2011-11-24 12:00:13
【问题描述】:

我需要在 Django 中为 Web 请求提供服务时启动一个 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 无法在 devserver 或 mod_wsgi 下创建输出文件。 This discussion 似乎表明操作系统信号和 pdftk 可能存在一些我不理解的更深层次的巫术。

是否有任何解决方法可以让这样的子进程调用在 wsgi 下正常工作?我避免使用 PyPDF 来合并 pdf 文件,因为我必须合并足够多的文件(数百个)以致内存不足(PyPDF 需要在合并时保持每个源 pdf 文件在内存中打开)。

我在最近的 Ubuntu,python 2.6 和 2.7 下这样做。

【问题讨论】:

    标签: python django subprocess mod-wsgi pdftk


    【解决方案1】:

    尝试使用绝对文件系统路径来输入和输出文件。 Apache 下的当前工作目录与运行服务器的目录不同,可以是任何目录。


    消除明显的第二次尝试。

    pdftk 程序是一个 Java 程序,它依靠能够生成/接收 SIGPWR 信号来触发垃圾收集或执行其他操作。问题是在守护进程模式下的 Apache/mod_wsgi 下,信号在请求处理程序线程中被阻塞,以确保它们仅由寻找进程关闭触发事件的主线程接收。当您派生进程以运行 pdftk 时,不幸的是,它从请求处理程序线程继承了阻塞的 sigmask。这样做的后果是,它阻碍了 Java 垃圾回收进程的运行,并导致 pdftk 以奇怪的方式失败。

    唯一的解决方案是使用 Celery 并让前端向 Celery 队列提交作业,以便 celeryd 然后 fork 并执行 pdftk。因为这是从与 Apache 不同的进程创建的,所以您不会遇到此问题。

    有关 mod_wsgi 和 pdftk 的更多详细信息请参见 Google,尤其是在 Google Groups 中。

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

    【讨论】:

    • 我实际上使用的是绝对路径,谢谢。我已经更新了示例代码以反映这一点。不幸的是,问题仍然存在。
    • 谢谢,分叉芹菜工作。我同步执行了 celery 任务(使用task.delay().get(),这样它就可以在一个响应周期内发生,这让我得到了想要的结果。
    • 哇,我在其他任何地方都找不到这个解决方案,但这正是我必须做的才能让 PDFTK 工作。非常感谢!
    【解决方案2】:

    更新:在 Python 3 上使用 Pdftk 合并两个 Pdf:

    这个问题发布已经好几年了。 (2011)。 原发帖人说 os.system 在他们运行旧版本的 python 时对他们不起作用:

    • Python 2.6
    • Python 2.7

    Python 3.4 上,os.system 为我工作:

    • 导入操作系统
    • 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.')
    

    库:

    【讨论】:

      猜你喜欢
      • 2012-02-27
      • 2014-06-25
      • 2016-10-08
      • 1970-01-01
      • 2017-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-23
      相关资源
      最近更新 更多