Python получает файл из приложения на лету (без сохранения его в файловой системе) - PullRequest
0 голосов
/ 28 сентября 2018

Я хочу позволить пользователю отправить файл 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()

Я нашел Загрузите объект StringIO с помощью send_file () , но эта ошибка сохраняетсяк рамке колбы.Я скорее использую web2py framework .

Обновление 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 голосов
/ 28 сентября 2018

Я бы начал с сохранения в файл-подобный объект и затем скопировал бы этот файл-объект в файл (локально, не загружая его).Это должно делить пополам диапазон, где происходит проблема.Кстати, я бы использовал 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 от вашего метода загрузки.То, что вы получили бы здесь, это последовательность байтов.Если он ожидает файлоподобный объект или, возможно, путь, это тоже может быть проблемой.

Перемещение указателя файла в начало (с использованием out_stream.seek (0)) будет уместно, только если вы возвращаетефайлоподобный объект, например return out_stream вместо return outstream.getvalue().Последний возвращает bytes, который, конечно, не имеет указателя файла.BytesIO (или StringIO) .getvalue () не требует установки курсора файла;он всегда возвращает полное содержимое объекта.

Кроме того, вместо того, чтобы полагаться на contenttype, чтобы получить его правильно, я бы прописал заголовок типа контента как: application/vnd.openxmlformats-officedocument.wordprocessingml.document.Если тип содержимого неправильно определил файл как файл формата .doc (до Word 2007), а не как файл формата .docx (Word 2007 и более поздние версии), это также может вызвать проблему.

...