Добавить папку в gzip в памяти с помощью python

У меня есть файл tar.gz, загруженный с s3, я загружаю его в память и хочу добавить папку и в итоге записать ее в другой s3.
Я пробовал разные подходы:

from io import BytesIO
import gzip
buffer = BytesIO(zip_obj.get()["Body"].read())
im_memory_tar = tarfile.open(buffer, mode='a')

Выше возникает ошибка: ReadError: invalid header .

При следующем подходе:

im_memory_tar = tarfile.open(fileobj=buffer, mode='a')
im_memory_tar.add(name='code_1', arcname='code') 

Содержимое кажется перезаписанным.
Знаете ли вы хорошее решение для добавления папки в файл tar.gz?
Спасибо.


person 3nomis    schedule 08.01.2021    source источник


Ответы (2)


очень хорошо объяснено в вопросе

Обратите внимание, что «a:gz» или «a:bz2» невозможны. Если режим не подходит для открытия определенного (сжатого) файла для чтения, выдается ReadError. Используйте режим 'r', чтобы избежать этого. Если метод сжатия не поддерживается, возникает CompressionError.

person samtoddler    schedule 08.01.2021

Сначала нам нужно рассмотреть, как добавить в файл tar. Отложим на время сжатие.

Файл tar завершается двумя 512-байтовыми блоками, состоящими только из нулей. Чтобы добавить больше записей, вам нужно удалить или перезаписать эти 1024 байта в конце. Если вы затем добавите туда еще один tar-файл или начнете писать там новый tar-файл, у вас будет один tar-файл со всеми записями исходных двух.

Теперь вернемся к tar.gz. Вы можете просто распаковать весь файл .gz, добавить его, как указано выше, а затем повторно сжать все это.

Избежать декомпрессии и рекомпрессии гораздо сложнее, так как нам пришлось бы как-то удалить эти последние 1024 байта нулей из конца сжатого потока. Это возможно, но вам потребуются некоторые знания о внутренностях сжатого потока с дефляцией.

Поток deflate состоит из серии сжатых блоков данных, каждый из которых имеет произвольное количество битов. Вам нужно будет распаковать, не записывая результат, пока вы не доберетесь до блока, содержащего последние 1024 байта. Вам нужно будет сохранить распакованный результат этого и любых последующих блоков, а также с какого бита в потоке начался этот блок. Затем вы можете повторно сжать эти данные, за исключением последних 1024 байтов, начиная с этого байта.

Завершите сжатие и запишите трейлер gzip, удалив 1024 нуля из CRC и длины. (Есть способ убрать нули из CRC.) Теперь у вас есть полный поток gzip для предыдущего файла .tar.gz, но с удаленными последними 1024 байтами нулей.

Поскольку объединение двух потоков gzip само по себе является действительным потоком gzip, теперь вы можете напрямую объединить второй файл .tar.gz или начать запись в нем нового потока .tar.gz. Теперь у вас есть один действительный поток .tar.gz с записями из двух исходных источников.

person Mark Adler    schedule 08.01.2021