Working
This commit is contained in:
parent
4e389210dc
commit
9d420793ec
31
archive.py
31
archive.py
|
|
@ -76,7 +76,7 @@ class ArchiveSender:
|
||||||
|
|
||||||
|
|
||||||
class TarSender(ArchiveSender):
|
class TarSender(ArchiveSender):
|
||||||
def response(self):
|
def generator(self):
|
||||||
def generate():
|
def generate():
|
||||||
for name, file in self.files.items():
|
for name, file in self.files.items():
|
||||||
yield tar_header_chunk(name, file)
|
yield tar_header_chunk(name, file)
|
||||||
|
|
@ -96,9 +96,11 @@ class TarSender(ArchiveSender):
|
||||||
# Because tar use records of 512 bytes, we need to pad the
|
# Because tar use records of 512 bytes, we need to pad the
|
||||||
# file with zeroes to fill the last chunk
|
# file with zeroes to fill the last chunk
|
||||||
yield b'\x00' * (512 - bytes_sent % 512)
|
yield b'\x00' * (512 - bytes_sent % 512)
|
||||||
|
return generate()
|
||||||
|
|
||||||
|
def response(self):
|
||||||
return Response(
|
return Response(
|
||||||
generate(),
|
self.generator(),
|
||||||
mimetype='application/x-tar',
|
mimetype='application/x-tar',
|
||||||
headers={'Content-Disposition': 'attachment; filename="archive.tar"'}
|
headers={'Content-Disposition': 'attachment; filename="archive.tar"'}
|
||||||
)
|
)
|
||||||
|
|
@ -131,9 +133,9 @@ def zip_local_file_header(filename: str, filepath: str) -> bytes:
|
||||||
# Field 4: compression mode (buffer[8:10]), leave at 0 (uncompressed)
|
# Field 4: compression mode (buffer[8:10]), leave at 0 (uncompressed)
|
||||||
|
|
||||||
# Field 5: file last modification time (buffer[10:14])
|
# Field 5: file last modification time (buffer[10:14])
|
||||||
mtime = datetime.fromtimestramp(stat.st_mtime)
|
mtime = datetime.fromtimestamp(stat.st_mtime)
|
||||||
buffer[10:12] = (mtime.second // 2) | (mtime.minute << 5) | (mtime.hour << 11)
|
buffer[10:12] = ((mtime.second // 2) | (mtime.minute << 5) | (mtime.hour << 11)).to_bytes(2, byteorder='little')
|
||||||
buffer[12:14] = mtime.day | (mtime.month << 5) | ((mtime.year - 1980) << 9)
|
buffer[12:14] = (mtime.day | (mtime.month << 5) | ((mtime.year - 1980) << 9)).to_bytes(2, byteorder='little')
|
||||||
|
|
||||||
# Field 6: crc-32 of uncompressed data (buffer[14:18])
|
# Field 6: crc-32 of uncompressed data (buffer[14:18])
|
||||||
buffer[14:18] = crc32(filepath).to_bytes(4, byteorder='little')
|
buffer[14:18] = crc32(filepath).to_bytes(4, byteorder='little')
|
||||||
|
|
@ -161,7 +163,7 @@ def zip_central_directory_file_header(filename: str, filepath: str, offset: int)
|
||||||
stat = os.stat(filepath)
|
stat = os.stat(filepath)
|
||||||
|
|
||||||
# Field 1: central directory file header signature (buffer[0:4])
|
# Field 1: central directory file header signature (buffer[0:4])
|
||||||
buffer[0:4] = b'\x02\x01\x4b\x50'
|
buffer[0:4] = b'\x50\x4b\x01\x02'
|
||||||
|
|
||||||
# Field 2: version made by (buffer[4:6])
|
# Field 2: version made by (buffer[4:6])
|
||||||
buffer[4:6] = b'\x0a'
|
buffer[4:6] = b'\x0a'
|
||||||
|
|
@ -174,9 +176,9 @@ def zip_central_directory_file_header(filename: str, filepath: str, offset: int)
|
||||||
# Field 4: compression mode (buffer[10:12]), leave at 0 (uncompressed)
|
# Field 4: compression mode (buffer[10:12]), leave at 0 (uncompressed)
|
||||||
|
|
||||||
# Field 5: file last modification time (buffer[12:16])
|
# Field 5: file last modification time (buffer[12:16])
|
||||||
mtime = datetime.fromtimestramp(stat.st_mtime)
|
mtime = datetime.fromtimestamp(stat.st_mtime)
|
||||||
buffer[12:14] = (mtime.second // 2) | (mtime.minute << 5) | (mtime.hour << 11)
|
buffer[12:14] = ((mtime.second // 2) | (mtime.minute << 5) | (mtime.hour << 11)).to_bytes(2, byteorder='little')
|
||||||
buffer[14:16] = mtime.day | (mtime.month << 5) | ((mtime.year - 1980) << 9)
|
buffer[14:16] = (mtime.day | (mtime.month << 5) | ((mtime.year - 1980) << 9)).to_bytes(2, byteorder='little')
|
||||||
|
|
||||||
# Field 6: crc-32 of uncompressed data (buffer[16:20])
|
# Field 6: crc-32 of uncompressed data (buffer[16:20])
|
||||||
buffer[16:20] = crc32(filepath).to_bytes(4, byteorder='little')
|
buffer[16:20] = crc32(filepath).to_bytes(4, byteorder='little')
|
||||||
|
|
@ -211,7 +213,7 @@ def zip_central_directory_file_header(filename: str, filepath: str, offset: int)
|
||||||
def zip_end_of_central_directory(items_number: int, central_directory_size: int, central_directory_offset: int):
|
def zip_end_of_central_directory(items_number: int, central_directory_size: int, central_directory_offset: int):
|
||||||
buffer = bytearray(22)
|
buffer = bytearray(22)
|
||||||
# Field 1: End of central directory signature = 0x06054b50 (buffer[0:4])
|
# Field 1: End of central directory signature = 0x06054b50 (buffer[0:4])
|
||||||
buffer[0:4] = b'\x06\x05\x4b\x50'
|
buffer[0:4] = b'\x50\x4b\x05\x06'
|
||||||
|
|
||||||
# Field 2: Number of this disk (buffer[4:6])
|
# Field 2: Number of this disk (buffer[4:6])
|
||||||
|
|
||||||
|
|
@ -236,7 +238,7 @@ def zip_end_of_central_directory(items_number: int, central_directory_size: int,
|
||||||
|
|
||||||
|
|
||||||
class ZipSender(ArchiveSender):
|
class ZipSender(ArchiveSender):
|
||||||
def response(self):
|
def generator(self):
|
||||||
def generate():
|
def generate():
|
||||||
local_offsets = dict()
|
local_offsets = dict()
|
||||||
current_byte = 0
|
current_byte = 0
|
||||||
|
|
@ -244,7 +246,7 @@ class ZipSender(ArchiveSender):
|
||||||
for name, file in self.files.items():
|
for name, file in self.files.items():
|
||||||
local_offsets[name] = current_byte
|
local_offsets[name] = current_byte
|
||||||
chunk = zip_local_file_header(name, file)
|
chunk = zip_local_file_header(name, file)
|
||||||
current_byte += chunk
|
current_byte += len(chunk)
|
||||||
|
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
|
|
@ -269,8 +271,11 @@ class ZipSender(ArchiveSender):
|
||||||
|
|
||||||
yield zip_end_of_central_directory(len(self.files.items()), central_directory_size, centra_directory_offset)
|
yield zip_end_of_central_directory(len(self.files.items()), central_directory_size, centra_directory_offset)
|
||||||
|
|
||||||
|
return generate()
|
||||||
|
|
||||||
|
def response(self):
|
||||||
return Response(
|
return Response(
|
||||||
generate(),
|
self.generator(),
|
||||||
mimetype='application/zip',
|
mimetype='application/zip',
|
||||||
headers={'Content-Disposition': 'attachment; filename="archive.zip"'}
|
headers={'Content-Disposition': 'attachment; filename="archive.zip"'}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue