python
  • django
  • pdf
  • popen
  • wkhtmltopdf
  • 2011-03-24 78 views 1 likes 
    1

    我在問一個與this one非常類似的問題。我在Django的Ubuntu服務器上使用wkhtmltopdf創建pdf。在Django中返回一個PDF響應

    from tempfile import * 
    from subprocess import Popen, PIPE 
    
    tempfile = gettempdir()+"/results.pdf" 
    papersize = 'Tabloid' 
    orientation = 'Landscape' 
    command_args = "wkhtmltopdf -O %s -s %s -T 0 -R 0 -B 0 -L 0 http://pdfurl %s" %(orientation, papersize, tempfile) 
    popen = Popen(command_args, stdout=PIPE, stderr=PIPE) 
    pdf_contents = popen.stdout().read() 
    popen.terminate() 
    popen.wait() 
    response = HttpResponse(pdf_contents, mimetype='application/pdf') 
    return response 
    

    這給了我一個「沒有這樣的文件或目錄」在popen = Popen ...線上的錯誤。所以我改變該行

    popen = Popen(["sh", "-c", command_args], stdout=PIPE, stderr=PIPE) 
    

    ,現在我得到一個「‘文件’對象不是可調用的」錯誤的pdf_contents = ...行。

    我也試着在popen = ...行添加.communicate(),但我似乎無法找到這種方式的PDF輸出。我應該補充一點,在命令行中輸入command_args行創建pdf就好了。任何人都可以將我指向正確的方向嗎?

    +0

    當你手動運行此命令,是將其輸出到控制檯?還是僅限於gettempdir()+「/ results.pdf」文件? – 2011-03-24 19:24:25

    +0

    它輸出到results.pdf文件,正確格式化和一切。 – buken 2011-03-24 19:32:44

    回答

    2

    你的第一個版本因爲python不知道wkhtmltopdf的位置而失敗。 Python不會檢查你的路徑。你的第二個版本將命令傳遞給一個處理這個問題的shell。你通過傳遞一個shell = True參數來達到同樣的效果。

    第二個問題(正如其他人已經注意到的)是,當你不應該的時候調用stdout()。

    第三個問題是您的wkhtmltopdf命令錯誤。你正在做的:

    相反,你應該通過

    wkhtmltopdf -O %s -s %s -T 0 -R 0 -B 0 -L 0 http://pdfurl - 
    

    這樣wkhtmltopdf將輸出寫入到標準輸出,你可以閱讀。如果你傳遞另一個 - 作爲源代碼,你可以通過標準輸入發送html。

    +0

    感謝您的快速回復。這幾乎是python文件I/O的崩潰過程。我最終將輸出直接傳遞到標準輸出並繞過任何臨時文件以避免安全事故。 – buken 2011-03-24 20:31:52

    0

    您可能需要考慮改變

    popen = Popen(command_args, stdout=PIPE, stderr=PIPE) 
    pdf_contents = popen.stdout().read() 
    # ... 
    response = ... 
    

    pdf_contents = subprocess.check_output(command_args.split()) 
    response = ... 
    

    或在舊版本:

    process = Popen(command_args.split(), stdout=PIPE, stderr=PIPE) 
    pdf_contents = process.stdout.read() 
    response = ... 
    

    我建議你看一看的check_output功能。

    編輯:另外,不要調用terminate(),因爲它會殺死進程而不等待它完成,可能會導致損壞的PDF。你幾乎只需要使用wait(),因爲它會等待進程完成(並輸出所有必須輸出的內容)。當使用check_output()函數時,您不必擔心它,因爲它會通過「default」等待進程完成。

    除此之外,命名與模塊名稱相同的變量(我正在談論tempfile)是一個不好的想法。我建議您將其更改爲tmpfile並檢出NamedTemporaryFile s,因爲它比現在更安全。

    +0

    不幸的是,這個調用在Python 2.7中是新的,我正在運行2.6。如果一切都失敗,我可能會嘗試升級。 – buken 2011-03-24 19:18:20

    +0

    感謝您的提示。我已經改變了tempfile變量的名字,並擺脫了terminate()。然而,儘管我看到服務器上正確的pdf文件,但我仍然收到一個空白的pdf文件。 – buken 2011-03-24 19:28:05

    +0

    如果這仍然不起作用,則可能需要在pdf_contents = process.stdout.read()之前添加process.wait()。希望能幫助到你。 – brahle 2011-03-24 19:36:23

    1

    你得到的原因'file' object is not callable是因爲一旦你有你的popen對象,stdout是一個文件句柄,而不是一個方法。不要叫,只需要使用它:

    popen = Popen(command_args, stdout=PIPE, stderr=PIPE) 
    pdf_contents = popen.stdout.read() 
    
    +0

    非常感謝。但現在,而不是錯誤,我回來了一個空白的pdf文件(0字節)。我可以看到坐在服務器上的正確的pdf。任何想法爲什麼「sh」,「-c」參數擺脫「沒有這樣的文件或目錄」錯誤? – buken 2011-03-24 19:20:28

    3

    wkhtmltopdf不輸出的PDF中的內容Popen讀它。 pdf_contents正確包含該命令的輸出(無)。你需要,如果你想將它返回給客戶端讀取輸出文件的內容(見下文),或跳過輸出文件,並進行wkhtmltopdf直接輸出PDF格式的內容,

    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') 
    
    1

    我意識到這不使用wkhtmltopdf,但我覺得這是更清潔。

    看看https://docs.djangoproject.com/en/dev/howto/outputting-pdf/

    相關問題