This commit is contained in:
Thomas Forgione 2024-07-29 10:50:44 +02:00
parent 8b2f968c2d
commit 74ff05d9c9
1 changed files with 67 additions and 4 deletions

View File

@ -104,7 +104,7 @@ class TarSender(ArchiveSender):
)
def crc32(filename):
def crc32(filename) -> int:
with open(filename, 'rb') as fh:
hash = 0
while True:
@ -115,7 +115,7 @@ def crc32(filename):
return hash
def zip_local_file_header(filename: str, filepath: str, fileindex: int) -> bytes:
def zip_local_file_header(filename: str, filepath: str) -> bytes:
buffer_size = 30 + len(filename)
buffer = bytearray(buffer_size)
stat = os.stat(filepath)
@ -145,7 +145,7 @@ def zip_local_file_header(filename: str, filepath: str, fileindex: int) -> bytes
buffer[22:26] = stat.st_size.to_bytes(4)
# Field 9: filename length (buffer[26:28])
buffer[26:29] = len(filename).to_bytes(4)
buffer[26:28] = len(filename).to_bytes(4)
# Field 10: extra field length (buffer[28:30])
@ -155,10 +155,73 @@ def zip_local_file_header(filename: str, filepath: str, fileindex: int) -> bytes
return buffer
def zip_central_directory_file_header(filename: str, filepath: str) -> bytes:
buffer_size = 46 + len(filename)
buffer = bytearray(buffer_size)
stat = os.stat(filepath)
# Field 1: central directory file header signature (buffer[0:4])
buffer[0:4] = b'\x02\x01\x4b\x50'
# Field 2: version made by (buffer[4:6])
buffer[4:6] = b'\x0a'
# Field 3: version needed to extract (minimum) (buffer[6:8])
buffer[6:8] = b'\x0a'
# Field 3: general purpose bit flag (buffer[8:10]), leave at 0
# Field 4: compression mode (buffer[10:12]), leave at 0 (uncompressed)
# Field 5: file last modification time (buffer[12:16])
mtime = datetime.fromtimestramp(stat.st_mtime)
buffer[12:14] = (mtime.second // 2) | (mtime.minute << 5) | (mtime.hour << 11)
buffer[14:16] = mtime.day | (mtime.month << 5) | ((mtime.year - 1980) << 9)
# Field 6: crc-32 of uncompressed data (buffer[16:20])
buffer[16:20] = crc32(filepath).to_bytes(4)
# Field 7: compressed size (buffer[20:24])
buffer[20:24] = stat.st_size.to_bytes(4)
# Field 8: uncompressed size (buffer[24:28])
buffer[24:28] = stat.st_size.to_bytes(4)
# Field 9: filename length (buffer[28:30])
buffer[28:30] = len(filename).to_bytes(4)
# Field 10: extra field length (buffer[30:32])
# Field 11: file comment length (buffer[32:34])
# Field 12: disk number where file starts (buffer[34:36])
# Field 13: internal file attributes (buffer[36:38])
# Field 14: external file attributes (buffer[38:42])
# Field 15: relative offset of the local file header (buffer[42:46])
# Field 16: filename (buffer[46:46+len(filename)])
buffer[46:46+len(filename)] = filename.encode('ascii')
return buffer
class ZipSender(ArchiveSender):
def response(self):
def generate():
yield 'oops'
for name, file in self.files.items():
yield zip_local_file_header(name, file)
with open(file, 'rb') as f:
while True:
bytes = f.read(CHUNK_SIZE)
if len(bytes) == 0:
break
yield bytes
return Response(
generate(),