Python 3 Ошибка памяти Zipfile. ожидаемый аргумент строки, полученный 'bytes'

У меня есть следующий код для создания zip файла в памяти, который вызывает ошибку, запущенную в Python 3.

from io import StringIO
from pprint import pprint
import zipfile


in_memory_data = StringIO()
in_memory_zip = zipfile.ZipFile(
    in_memory_data, "w", zipfile.ZIP_DEFLATED, False)
in_memory_zip.debug = 3

filename_in_zip = 'test_filename.txt'
file_contents = 'asdf'

in_memory_zip.writestr(filename_in_zip, file_contents)

Чтобы быть понятным, это только проблема Python 3. Я могу запустить код на Python 2. Точнее, я использую Python 3.4.3. Трассировка стека ниже:

Traceback (most recent call last):
  File "in_memory_zip_debug.py", line 14, in <module>
    in_memory_zip.writestr(filename_in_zip, file_contents)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/zipfile.py", line 1453, in writestr
    self.fp.write(zinfo.FileHeader(zip64))
TypeError: string argument expected, got 'bytes'
Exception ignored in: <bound method ZipFile.__del__ of <zipfile.ZipFile object at 0x1006e1ef0>>
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/zipfile.py", line 1466, in __del__
    self.close()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/zipfile.py", line 1573, in close
    self.fp.write(endrec)
TypeError: string argument expected, got 'bytes'

Ответы

Ответ 1

ZipFile записывает свои данные в виде байтов, а не строк. Это означает, что вам придется использовать BytesIO вместо StringIO в Python 3.

Различие между байтами и строками является новым в Python 3. Библиотека совместимости six имеет класс BytesIO для Python 2, если вы хотите, чтобы ваша программа была совместима с обоими.

Ответ 2

Проблема в том, что io.StringIO() используется в качестве буфера памяти, когда он должен быть io.BytesIO. Ошибка возникает из-за того, что код zipfile в конечном итоге вызывает StringIO(). Write() с байтами, когда StringIO ожидает строку.

Как только он изменился на BytesIO(), он работает:

from io import BytesIO
from pprint import pprint
import zipfile


in_memory_data = BytesIO()
in_memory_zip = zipfile.ZipFile(
    in_memory_data, "w", zipfile.ZIP_DEFLATED, False)
in_memory_zip.debug = 3

filename_in_zip = 'test_filename.txt'
file_contents = 'asdf'

in_memory_zip.writestr(filename_in_zip, file_contents)