список файлов внутри ZIP-файла более 4 ГБ, не загружая все это - PullRequest
2 голосов
/ 05 февраля 2020

Я пытаюсь перечислить все файлы в файле ".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 ГБ имеют другую структуру

Zip64 file structure

И в соответствии со спецификацией (поиск по «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)

Что я делаю неправильно?

1 Ответ

0 голосов
/ 05 февраля 2020

Я написал zipdetails долгое время go, чтобы помочь мне понять внутреннюю структуру zip-файлов.

Давайте создадим zip-файл zip64 (опция -fz заставит Zip64).

$ zip -fz xx.zip /tmp/Makefile

$ unzip -l xx.zip
Archive:  xx.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
     1240  02-05-2020 14:31   tmp/Makefile
---------                     -------
     1240                     1 file

Если вы запустите zipdetails для zip-файла zip64 и посмотрите, где находятся данные центрального каталога, вы увидите нечто подобное. Я добавил дополнительную аннотацию, чтобы показать значение указателя, которое вам нужно установить. Поэтому вам нужно установить поле " относительное смещение конца zip64 записи центрального каталога ", чтобы оно указывало на расположение поля " Zip64 end of locator центрального каталога". В данном случае это гекс 299.

0299 ZIP64 END CENTRAL DIR 06064B50  <----------------+ 
     RECORD                                           |
029D Size of record        000000000000002C           |
02A5 Created Zip Spec      1E '3.0'                   |
02A6 Created OS            03 'Unix'                  |
02A7 Extract Zip Spec      2D '4.5'                   |
02A8 Extract OS            00 'MS-DOS'                |
02A9 Number of this disk   00000000                   |
02AD Central Dir Disk no   00000000                   |
02B1 Entries in this disk  0000000000000001           |
02B9 Total Entries         0000000000000001           |
02C1 Size of Central Dir   000000000000005E           |
02C9 Offset to Central dir 000000000000023B           |
                                                      |
02D1 ZIP64 END CENTRAL DIR 07064B50                   |
     LOCATOR                                          |
02D5 Central Dir Disk no   00000000                   |
02D9 Offset to Central dir 0000000000000299  ---------+
02E1 Total no of Disks     00000001

02E5 END CENTRAL HEADER    06054B50
02E9 Number of this disk   0000
02EB Central Dir Disk no   0000
02ED Entries in this disk  0001
02EF Total Entries         0001
02F1 Size of Central Dir   0000005E
02F5 Offset to Central Dir FFFFFFFF
02F9 Comment Length        0000
Done
...