2017-09-02 56 views
1

我有一個從模板生成DOCX文件然後通過Flask提供的任務。我使用的是python-docx-templates,它只是一個圍繞python-docx的包裝,允許使用jinja模板。如何在Python中生成DOCX並將其保存在內存中?

在他們建議使用StringIO從中只保存在內存中的文件結束,所以這是我的代碼看起來像:

def report_doc(user_id): 
    # Prepare the data... 

    from docxtpl import DocxTemplate 

    doc = DocxTemplate(app.root_path+'/templates/report.docx') 
    doc.render({ 
     # Pass parameters 
    }) 
    from io import StringIO 
    file_stream = StringIO() 
    doc.save(file_stream) 

    return send_file(file_stream, as_attachment=True, attachment_filename='report_'+user_id+'.docx') 

在保存它拋出一個錯誤TypeError: string argument expected, got 'bytes'。谷歌搜索後,我發現this answer其中說,ZipFile期待BytesIO。但是,當我用String替換StringIO時,它只返回一個空文件,所以它不會拋出任何錯誤,但肯定不會保存文件。

在這種情況下究竟會發揮什麼作用?如果在這裏完全有問題,一般情況下可以如何工作?

謝謝!

UPD:這裏有完整的跟蹤的例外save函數調用:

File "/ms/controllers.py", line 1306, in report_doc 
    doc.save(file_stream) 
    File "/.env/lib/python3.5/site-packages/docx/document.py", line 142, in save 
    self._part.save(path_or_stream) 
    File "/.env/lib/python3.5/site-packages/docx/parts/document.py", line 129, in save 
    self.package.save(path_or_stream) 
    File "/.env/lib/python3.5/site-packages/docx/opc/package.py", line 160, in save 
    PackageWriter.write(pkg_file, self.rels, self.parts) 
    File "/.env/lib/python3.5/site-packages/docx/opc/pkgwriter.py", line 33, in write 
    PackageWriter._write_content_types_stream(phys_writer, parts) 
    File "/.env/lib/python3.5/site-packages/docx/opc/pkgwriter.py", line 45, in _write_content_types_stream 
    phys_writer.write(CONTENT_TYPES_URI, cti.blob) 
    File "/.env/lib/python3.5/site-packages/docx/opc/phys_pkg.py", line 155, in write 
    self._zipf.writestr(pack_uri.membername, blob) 
    File "/usr/lib/python3.5/zipfile.py", line 1581, in writestr 
    self.fp.write(zinfo.FileHeader(zip64)) 
TypeError: string argument expected, got 'bytes' 
+0

你能否提供你的異常的全部回溯? – stamaimer

回答

2

使用BytesIO實例是正確的,但是你把它傳遞給send_file之前需要rewind the file pointer

製作確保在調用send_file()之前,文件指針位於發送數據的起始位置 。

所以這應該工作:

import io 
from docxtpl import DocxTemplate 

def report_doc(user_id): 
    # Prepare the data... 

    doc = DocxTemplate(app.root_path+'/templates/report.docx') 
    doc.render({ 
     # Pass parameters 
    }) 
    file_stream = io.BytesIO() 
    doc.save(file_stream) 
    file_stream.seek(0) 

    return send_file(file_stream, as_attachment=True, attachment_filename='report_'+user_id+'.docx') 

(測試在Firefox上,我發現瀏覽器保存檢索從緩存中的文件,即使我指定一個不同的文件名,所以你可能需要清除瀏覽器的緩存同時測試或禁用開發工具中的緩存(如果瀏覽器支持此功能),或者調整Flask的cache control settings)。

相關問題