Я пытаюсь перечислить все файлы в файле ".zip", не загружая их все.
Мне удалось сделать это с файлами размером менее 4 ГБ со следующим кодом:
def get_list_of_files_from_zip(self, source_bucket, source_key, ignore_hidden_files=True):
# self.s3 returns boto3.resource('s3') already initialize with the keys
s3_object = self.s3.Object(source_bucket, source_key)
size = s3_object.content_length
# End of central directory record (EOCD)
eocd = self._fetch_bytes_from_file(source_bucket, source_key, size - 22, 22)
# start offset and size of the central directory
cd_start = convert_to_int(eocd[16:20])
cd_size = convert_to_int(eocd[12:16])
# fetch central directory, append EOCD, and open as zipfile!
cd = self._fetch_bytes_from_file(source_bucket, source_key, cd_start, cd_size)
zip = ZipFile(BytesIO(cd + eocd))
list_of_file = []
for entry in zip.filelist:
if ignore_hidden_files and (entry.file_size == 0 or is_hidden(entry.filename)):
continue
list_of_file.append({"name": entry.filename,
"size": entry.file_size}) # On bytes
return list_of_file
def _fetch_bytes_from_file(self, source_bucket, source_key, start, len):
"""
range-fetches a S3 key
"""
end = start + len - 1
s3_object = self.s3.Object(source_bucket, source_key).get(Range="bytes=%d-%d" % (start, end))
return s3_object['Body'].read()
def convert_to_int(bytes):
val = ord(bytes[0]) + (ord(bytes[1]) << 8)
if len(bytes) > 3:
val += (ord(bytes[2]) << 16) + (ord(bytes[3]) << 24)
return val
Проблема в том, что я пытался сделать то же самое с файлом объемом 70 ГБ, и я получаю следующее:
Traceback (most recent call last):
File "/Users/.../env/lib/python2.7/site-packages/IPython/core/interactiveshell.py", line 3035, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-7-b4cd8dc7616e>", line 1, in <module>
s3.get_list_of_files_from_zip(bucket_name,key_name)
File "/Users/.../base.py", line 153, in get_list_of_files_from_zip
zip = ZipFile(BytesIO(cd + eocd))
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/zipfile.py", line 770, in __init__
self._RealGetContents()
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/zipfile.py", line 839, in _RealGetContents
raise BadZipfile("Bad magic number for central directory")
BadZipfile: Bad magic number for central directory
После некоторых исследований я обнаружил, что zip-файлы размером более 4 ГБ имеют другую структуру
И в соответствии со спецификацией (поиск по «4.3.15 Zip64 end of the locator» )
"Конец Zip64 локатора центрального каталога" должен помочь мне найти "Конец записи центрального каталога Zip64" , который позволит мне извлечь начало конечная длина центрального каталога файла zip64.
Итак, что я сделал:
size_eocd = 22 # End of central directory record
size_Zip64EndCD = 20
Zip64EndCD = self._fetch_bytes_from_file(source_bucket, source_key, size - (size_eocd + size_Zip64EndCD), size_Zip64EndCD)
# relative offset of the zip64 end of central directory record 8 bytes
relative_offset = convert_to_int(Zip64EndCD[8:16])
# result in my example relative_offset = 1811690735, size = 74826134865
И здесь, где я теряюсь, документация говорит, что это «относительное смещение» конца почтового индекса 64 центрального каталога "но это не ' т относительно какого смещения (размер? позиция CD? ???)
Я попробую следующее, но я не нахожу "конец zip64 центральной подписи dir" = 0x06064b50
"\x50\x4b\x06\x06" in self._fetch_bytes_from_file(source_bucket, source_key, size - relative_offset, 3000)
Что я делаю неправильно?