2017-09-02 171 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)。

相关问题