Python即时从应用程序获取文件(而不将其保存在文件系统中)

时间:2018-09-28 07:40:55

标签: python output python-docx on-the-fly

我想让用户向我的应用程序提交MS Word文件,使用python-docx库对其进行处理,然后将其返回。由于文件大小可能很大,因此我不想在处理后将其保存到文件系统中,而是将其退回下载。

从流中获取文件-可行

import docx
from docx.document import Document 
from StringIO import StringIO

source_stream = StringIO(request.vars['file'].value)
document = docx.Document(source_stream)
source_stream.close()
process_doc(document)

将其作为流返回-这不起作用

该应用程序确实使用户能够下载文件,但是* MS Word无法打开文件,并说“因为某些部分丢失或无效”

>
def download(document, filename):
    import contenttype as c
    import cStringIO
    out_stream = cStringIO.StringIO()
    document.save(out_stream)  

    response.headers['Content-Type'] = c.contenttype(filename)
    response.headers['Content-Disposition'] = \
            "attachment; filename=%s" %  filename
    return out_stream.getvalue()

我发现了Upload a StringIO object with send_file(),但这仍然存在于flask框架中。我宁愿使用 web2py框架

更新1

有人说过在输出流中发送文件指针之前将文件指针移动到文档数据的开始。但是怎么办呢?

更新2

按照@scanny的建议,我创建了一个空文件

document = docx.Document()

并使其使用BytesIO模块从文件对象下载:

document = docx.Document() 
from io import BytesIO
out_stream = BytesIO()
document.save(out_stream)
filename = 'temporal_file.docx'
filepath = os.path.join(request.folder, 'uploads',filename )
try:
    with open(filepath, 'wb') as f:
        f.write(out_stream.getvalue())
    response.flash ='Success to open file for writing'
    response.headers['Content-Disposition'] = "attachment; filename=%s" % filename
    response.headers['Content-Type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    #response['X-Sendfile'] = filepath
    #response['Content-Length'] = os.stat(filepath).st_size
    return  out_stream.getvalue()

如代码中所示,我还将空文件写入文件系统。而且,我可以轻松手动下载它并用MS Word打开enter image description here

因此,问题仍然存在。为什么下载的MS Word文件(通过输出流)已损坏并且无法被MS Word打开

更新3

我已经从文件输出到输出流的过程中消除了python-docx。结果是一样的:文件下载过程完成后,无法在MS Word中打开它。代码:

# we load without python-docx library
from io import BytesIO
try:
    filename = 'empty_file.docx'
    filepath = os.path.join(request.folder, 'uploads',filename )
    # read a file from file system (disk)
    with open(filepath, 'rb') as f: 
        out_stream = BytesIO(f.read())
    response.flash ='Success to open file for reading'
    response.headers['Content-Disposition'] = "attachment; filename=%s" % filename
    response.headers['Content-Type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    return out_stream.getvalue()
except Exception as e:
    response.flash ='Error open file for reading or download it - ' + filename
return

1 个答案:

答案 0 :(得分:1)

我将首先保存到类似文件的对象,然后将该类似文件的对象复制到文件(本地而不下载)。那应该将问题发生的范围一分为二。顺便说一下,我将使用BytesIO而不是StringIO。在2.7中可能不会有什么区别,但是可以,并且StringIO在任何情况下都无法在Python 3中工作:

from io import BytesIO

# ... code that processes `document`
out_stream = BytesIO()
document.save(out_stream)
with open('test.docx', 'wb') as f:
    f.write(out_stream.getvalue())

如果这不起作用(test.docx无法打开),则您已将问题范围缩小到document.save()调用之前。

如果有效有效,则可以再次尝试下载并查看,但要特别注意预期的类型,该类型应为下载方法中的return值。您在这里得到的是一个字节序列。如果期望有类似文件的对象或路径,那也可能是问题所在。

仅在返回类似文件的对象(例如return out_stream而不是return outstream.getvalue()的情况下,将文件指针移动到起始位置(使用out_stream.seek(0))才有意义。后者返回bytes,当然没有文件指针。 BytesIO(或StringIO).getvalue()不需要设置文件游标;它总是返回对象的全部内容。

此外,我不是依靠contenttype来解决问题,而是将内容类型标头拼写为:application/vnd.openxmlformats-officedocument.wordprocessingml.document。如果contenttype将文件误识别为.doc格式(Word 2007之前的版本)文件,而不是.docx格式(Word 2007及更高版本),则也可能会引起问题。