我希望此代码在与Python 2或Python 3一起运行时能正常工作
from zipfile import ZipFile, ZipInfo
with ZipFile("out.zip", 'w') as zf:
content = "content"
info = ZipInfo()
info.filename = "file.txt"
info.flag_bits = 0x800
info.file_size = len(content)
zf.writestr(info, content)
但是,在Python 2下,out.zip会启动:
50 4b 03 04 14 00 00 08
在Python3下,它将启动:
50 4b 03 04 14 00 00 00
不同之处是flag_bits
,对于Python 2设置为0x800
,对于Python 3则设置为0x00
。这就是BIT11:语言编码。 BIT11似乎被设置为if filename.encode("ascii")
抛出。
在创建ZipInfo对象后,我试图通过设置标志来强制启用此位,但是在0x00
中将其重置为_open_to_write()
。
我想知道这里是否有人有好的解决方案。理想情况下,我希望两个输出都具有标志集,因为这反映了jar实用程序的工作。
编辑:已更新,添加了info.flag_bits = 0x800
行,目的只是为了阐明我要实现的目标。我在Windows上重现了此内容:
ActivePython 3.6.0.3600,vs ActivePython 2.7.14.2717,Windows 10。
在Linux上:
Python 3.6.6和Python 2.7.11
如果很重要,我将按照我的示例运行此操作,不进行哈希爆炸,直接调用解释器:
pythonX test.py
答案 0 :(得分:1)
编辑:以下代码对我适用于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
可能是不正确的情况,尽管我不太清楚。
我无法重现此错误,但您是对的,如果文件名是Unicode并且编码为ASCII失败,那么标志位的第11位将被设置:
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对象后,我试图通过设置标志来强制启用此位,但是在_open_to_write()中将其重置为0x00。
如果我添加:
info.filename = "file.txt"
info.flag_bits |= 0x0800
(将文件名设置为u"schön"
之后)并在Python 2.7或3.6下运行,我得到了标头中设置的位(当然zip目录中的文件名改回了{{1}) }。
答案 1 :(得分:0)
我暂时正在使用类似的东西:
from zipfile import ZipFile, ZipInfo
import struct
orig_function = ZipInfo.FileHeader
def new_function(self, zip64=None):
header = orig_function(self, zip64)
fmt = "B"*len(header)
blist = list(struct.unpack(fmt, header))
blist[7] |= 0x8
return struct.pack(fmt, *blist)
setattr(ZipInfo, "FileHeader", new_function)
with ZipFile("out.zip", 'w') as zf:
content = "content"
info = ZipInfo()
info.filename = "file.txt"
info.file_size = len(content)
zf.writestr(info, content)
希望它不会很快中断,FileHeader()似乎将来不会改变。