обзор
July 12, 2021

2 способа упаковки Big Data в ручную кладь

Проблема передачи больших данных, их сжатия и разбиения набила оскомину для data scientist-ов. В этой статье поделюсь методами решения этих задач.

Рассмотрим кейс передачи данных на сервер, когда последовательность действий приобретает такой вид - архивация набора файлов, разбиение архива на куски фиксированного размера, передача, сборка на целевом сервере в один файл и его распаковка.

Изложим два способа.

С использованием утилит

Создать архив из папки можно командой:

zip -r архив.zip папка_файл

Чтобы разбить на части с верхней границей размера набираем:

zip цельный_архив.zip --out разделенный_архив.zip -s размер

Например, так указывается размер не более 50 Мб или 100 Кб - 50m и 100k (также имеются сокращения g - ГБ, t - ТБ)

Эти команды можно объединить:

zip -r -s размер разделенный_архив.zip папка_файл

Для сборки на сервере частей архива в один применяется команда:

zip -s 0 основной_файл_разделенного_архива.zip --out имя_собранного_архива.zip 

Распаковка может пройти командой unzip, если не возникнет проблем с кодировкой:

unzip имя_архива.zip

В противном случае (например, так как у меня названия файлов содержали символы кириллицы возникло исключение - Illegal byte sequence) используем команду:

ditto -V -x -k --sequesterRsrc --rsrc имя_архива.zip имя_папки_с_результатом 

С использование Python

Те же действия можно произвести с помощью Python. Для этого нужно внимательно изучить рекомендации изложенные в этом материале.

Коротко, вам потребуются два модуля - zipfile и filesplit, их классы ZipFile и Filesplit, а также методы с "говорящими" названиями. Приведу разбитый на две части код для реализации указанной в начале статьи цели по передаче папки на сервер (dir_source).

Операции на локальных машине (архивирование и разбиение на части):

import zipfile
import os
from fsplit.filesplit import Filesplit

dir_source = 'source'
arch_fn = 'data'
dir_pieces = 'pieces'

with zipfile.ZipFile(f'{arch_fn}.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zip_f:
   for folder, subfolders, filenames in os.walk(dir_source):
       for filename in filenames:
           zip_f.write(os.path.join(folder, filename))
print(f'создан архив с именем {arch_fn}.zip')

zip_f = zipfile.ZipFile(f'{arch_fn}.zip')
for fn in zip_f.namelist():
    info = zip_f.getinfo(fn)
    print(f'файл - {fn}, размер - {info.file_size}, сжатый размер - {info.compress_size}')

fs = Filesplit()
if not os.path.exists(dir_pieces):
    os.mkdir(dir_pieces)
    
def split_cb(f, s):
    print("file: {0}, size: {1}".format(f, s))
fs.split(file=f'{arch_fn}.zip', split_size=50000, output_dir=dir_pieces, callback=split_cb)

os.remove(f'{arch_fn}.zip')

Операции на сервере (сборка и извлечение):

import zipfile
import os
from fsplit.filesplit import Filesplit
import shutil

fs = Filesplit()

dir_pieces = 'pieces'
arch_fn = f'{dir_pieces}/data'
dir_out = 'output'

def merge_cb(f, s):
    print("file: {0}, size: {1}".format(f, s))

fs.merge(input_dir=dir_pieces, callback=merge_cb)

zip_f = zipfile.ZipFile(f'{arch_fn}.zip')
zip_f.extractall(dir_out)

shutil.rmtree(dir_pieces)