Редактировать: Вот код, который работает для меня с Python 2.7, но не с 3.6 (немного загадкой, казалось, сработал ранее сегодня вечером):
$ cat zipf.py
from __future__ import print_function
from zipfile import ZipFile, ZipInfo
with ZipFile("out.zip", 'w') as zf:
content = "content"
info = ZipInfo()
info.filename = "file.txt"
info.flag_bits = 0x800
# don't set info.file_size here: zf.writestr() does that
zf.writestr(info, content)
with open('out.zip', 'rb') as stream:
byteseq = stream.read(8)
for i in byteseq:
if isinstance(i, str): i = ord(i)
print('{:02x}'.format(i), end=' ')
print()
Запускать как:
$ python2.7 zipf.py
50 4b 03 04 14 00 00 08
но:
$ python3.6 zipf.py
50 4b 03 04 14 00 00 00
Конечно, можно заставить работать, убедившись, что файл открыт перед созданием записи info
. Однако тогда вы должны избегать writestr
, и это работает только с Python 3.6 (и выглядит довольно оскорбительно):
from __future__ import print_function
from zipfile import ZipFile, ZipInfo
with ZipFile("out.zip", 'w') as zf:
info = ZipInfo()
info.filename = "file.txt"
content = "content"
if not isinstance(content, bytes):
content = content.encode('utf8')
info.file_size = len(content)
with zf.open(info, 'w') as stream:
info.flag_bits = 0x800
stream.write(content)
with open('out.zip', 'rb') as stream:
byteseq = stream.read(8)
for i in byteseq:
if isinstance(i, str): i = ord(i)
print('{:02x}'.format(i), end=' ')
print()
Вероятно, дело в том, что 3,6 сброс всех info.flag_bits
(через внутренний open
, что он делает) просто неверен, хотя мне это не совсем понятно.
Оригинальный ответ ниже
Я не могу воспроизвести это, но вы правы, что бит 11 в битах флага установлен, если имя файла Unicode и кодировка ASCII завершается неудачно:
def _encodeFilenameFlags(self):
if isinstance(self.filename, unicode):
try:
return self.filename.encode('ascii'), self.flag_bits
except UnicodeEncodeError:
return self.filename.encode('utf-8'), self.flag_bits | 0x800
else:
return self.filename, self.flag_bits
(источник Python 2.7 zipfile.py) или:
def _encodeFilenameFlags(self):
try:
return self.filename.encode('ascii'), self.flag_bits
except UnicodeEncodeError:
return self.filename.encode('utf-8'), self.flag_bits | 0x800
(источник Python 3.6 zipfile.py).
Чтобы установить бит, вам нужно имя файла, которое не может быть закодировано напрямую в ASCII, например ::
info.filename = u"sch\N{latin small letter o with diaeresis}n" # "file.txt"
(эта нотация работает как с Python 2.7, так и с 3.6).
Я попытался включить этот бит, установив флаг после создания объекта ZipInfo, но он возвращается к 0x00 в _open_to_write ().
Если я добавлю:
info.filename = "file.txt"
info.flag_bits |= 0x0800
(сразу после установки имени файла u"schön"
) и запуска его в Python 2.7 или 3.6, я получаю бит, установленный в заголовке (конечно, имя файла в zip-каталоге меняется на file.txt
).