Модуль Zipfile для Python3.6: запись в байты вместо файлов для Odoo - PullRequest
0 голосов
/ 15 января 2019


Я пытался использовать модуль zipfile для Python 3.6 для создания ZIP-файла, который содержит несколько объектов.
Моя проблема в том, что мне нужно управлять файлами из базы данных Odoo, которая позволяет мне использовать только bytes объекты вместо файлов.

Это мой текущий код:

import zipfile

empty_zip_data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
zip = zipfile.ZipFile(empty_zip_data, 'w')

# files is a list of tuples: [(u'file_name', b'file_data'), ...]
for file in files:
    file_name = file[0]
    file_data = file[1]
    zip.writestr(file_name, file_data)

Что возвращает эту ошибку:

File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/zipfile.py", line 1658, in writestr
  with self.open(zinfo, mode='w') as dest:
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/zipfile.py", line 1355, in open
  return self._open_to_write(zinfo, force_zip64=force_zip64)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/zipfile.py", line 1468, in _open_to_write
  self.fp.write(zinfo.FileHeader(zip64))
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/zipfile.py", line 723, in write
  n = self.fp.write(data)
AttributeError: 'bytes' object has no attribute 'write'

Как я должен это сделать? Я следовал за ZipFile.writestr () документами , но это ни к чему не привело ...

РЕДАКТИРОВАТЬ: использование file_data = file[1].decode('utf-8') в качестве второго параметра также бесполезно, я получаю ту же ошибку.

Ответы [ 2 ]

0 голосов
/ 31 января 2019

Если вы хотите обработать все это в памяти без временного файла, тогда используйте io.BytesIO в качестве объекта файла для ZipFile:

import io
from zipfile import ZIP_DEFLATED, ZipFile

file = io.BytesIO()
with ZipFile(file, 'w', ZIP_DEFLATED) as zip_file:
    for name, content in [
        ('file.dat', b'data'), ('another_file.dat', b'more data')
    ]:
        zip_file.writestr(name, content)

zip_data = file.getvalue()
print(zip_data)

Вы также можете установить алгоритм сжатия, как показано, потому что в противном случае используется значение по умолчанию (без сжатия!).

0 голосов
/ 15 января 2019

Как уже упоминалось в моем комментарии, проблема заключается в этой строке:

empty_zip_data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
zip = zipfile.ZipFile(empty_zip_data, 'w')

Вы пытаетесь передать объект byte в метод ZipFile(), но, как и open(), он ожидает объект в виде пути.

В вашем случае вы можете использовать модуль tempfile (в этом конкретном примере мы будем использовать SpooledTemporaryFile из этого соответствующего вопроса :

import tempfile
import zipfile

# Create a virtual temp file
with tempfile.SpooledTemporaryFile() as tp:

    # pass the temp file for zip File to open
    with zipfile.ZipFile(tp, 'w') as zip:
        files = [(u'file_name', b'file_data'), (u'file_name2', b'file_data2'),]
        for file in files:
            file_name = file[0]
            file_data = file[1]
            zip.writestr(file_name, file_data)

    # Reset the cursor back to beginning of the temp file
    tp.seek(0)
    zipped_bytes = tp.read()

zipped_bytes
# b'PK\x03\x04\x14\x00\x00\x00\x00\x00\xa8U ... \x00\x00'

Обратите внимание на использование менеджеров контекста для обеспечения правильного закрытия всех файловых объектов после загрузки.

Это дает вам zipped_bytes, то есть байты, которые вы хотите передать обратно в Odoo. Вы также можете проверить zipped_bytes, записав его в физический файл, чтобы посмотреть, как он выглядит в первую очередь:

with open('test.zip', 'wb') as zf:
    zf.write(zipped_bytes)

Если вы обрабатываете файлы достаточно большого размера, обязательно обратите внимание и используйте аргумент max_size в документации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...