Skip to content

Instantly share code, notes, and snippets.

@VladRassokhin
Created November 14, 2024 20:29
Show Gist options
  • Save VladRassokhin/9299bb8fbe3169b96e7bc31f91553815 to your computer and use it in GitHub Desktop.
Save VladRassokhin/9299bb8fbe3169b96e7bc31f91553815 to your computer and use it in GitHub Desktop.
Generate Zip64 archive with data in zip64 end-of-archive record
import struct
import zipfile
from zipfile import *
structCentralDir = "<4s4B4HL2L5H2L"
stringCentralDir = b"PK\001\002"
sizeCentralDir = struct.calcsize(structCentralDir)
structEndArchive = b"<4s4H2LH"
stringEndArchive = b"PK\005\006"
sizeEndCentDir = struct.calcsize(structEndArchive)
structEndArchive64Locator = "<4sLQL"
stringEndArchive64Locator = b"PK\x06\x07"
sizeEndCentDir64Locator = struct.calcsize(structEndArchive64Locator)
structEndArchive64 = "<4sQ2H2L4Q"
stringEndArchive64 = b"PK\x06\x06"
sizeEndCentDir64 = struct.calcsize(structEndArchive64)
class SpecialZipFile(zipfile.ZipFile):
def _write_end_record(self):
# Write central directory
for zinfo in self.filelist:
dt = zinfo.date_time
dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
file_size = zinfo.file_size
compress_size = zinfo.compress_size
header_offset = zinfo.header_offset
extra_data = zinfo.extra
min_version = 0
extract_version = max(min_version, zinfo.extract_version)
create_version = max(min_version, zinfo.create_version)
filename, flag_bits = zinfo._encodeFilenameFlags()
centdir = struct.pack(structCentralDir,
stringCentralDir, create_version,
zinfo.create_system, extract_version, zinfo.reserved,
flag_bits, zinfo.compress_type, dostime, dosdate,
zinfo.CRC, compress_size, file_size,
len(filename), len(extra_data), len(zinfo.comment),
0, zinfo.internal_attr, zinfo.external_attr,
header_offset)
self.fp.write(centdir)
self.fp.write(filename)
self.fp.write(extra_data)
self.fp.write(zinfo.comment)
pos2 = self.fp.tell()
# Write end-of-zip-archive record
centDirCount = len(self.filelist)
centDirSize = pos2 - self.start_dir
centDirOffset = self.start_dir
# Write the ZIP64 end-of-archive records
if not self._allowZip64:
raise LargeZipFile("Require ZIP64 extensions")
additionalData = b"additional data at the end of ZIP64 EOCD record"
zip64endrec = struct.pack(
structEndArchive64, stringEndArchive64,
44 + len(additionalData), 45, 45, 0, 0, centDirCount, centDirCount,
centDirSize, centDirOffset)
self.fp.write(zip64endrec)
self.fp.write(additionalData)
zip64locrec = struct.pack(
structEndArchive64Locator,
stringEndArchive64Locator, 0, pos2, 1)
self.fp.write(zip64locrec)
centDirCount = min(centDirCount, 0xFFFF)
centDirSize = min(centDirSize, 0xFFFFFFFF)
centDirOffset = min(centDirOffset, 0xFFFFFFFF)
endrec = struct.pack(structEndArchive, stringEndArchive,
0, 0, centDirCount, centDirCount,
centDirSize, centDirOffset, 0)
self.fp.write(endrec)
if self.mode == "a":
self.fp.truncate()
self.fp.flush()
def run():
with SpecialZipFile("archive.zip", mode="w",
compression=ZIP_DEFLATED, compresslevel=0,
allowZip64=True) as zf:
with zf.open("file.txt", mode="w", force_zip64=True) as zi:
zi.write(b"content")
if __name__ == '__main__':
run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment