#python #stream #aws-lambda #zip
#python #поток #aws-lambda #zip
Вопрос:
Я хочу заархивировать поток и передать результат. Я делаю это с помощью AWS Lambda, что имеет значение в смысле доступного дискового пространства и других ограничений. Я собираюсь использовать архивированный поток для записи объекта AWS S3 с помощью upload_fileobj()
или put()
, если это имеет значение.
Я могу создавать архив в виде файла, пока у меня не появятся небольшие объекты:
import zipfile
zf = zipfile.ZipFile("/tmp/byte.zip", "w")
zf.writestr(filename, my_stream.read())
zf.close()
Для большого объема данных я могу создать объект вместо файла:
from io import BytesIO
...
byte = BytesIO()
zf = zipfile.ZipFile(byte, "w")
....
но как я могу передать заархивированный поток на вывод? Если я использую zf.close()
— поток будет закрыт, если я его не использую — архив будет неполным.
Ответ №1:
Вместо использования zipfile, встроенного в Python, вы можете использовать stream-zip (полное раскрытие: написано мной)
Если у вас есть итерация байтов, my_data_iter
скажем, вы можете получить итерацию zip-файла, используя его stream_zip
функцию:
from datetime import datetime
from stream_zip import stream_zip, ZIP_64
def files():
modified_at = datetime.now()
perms = 0o600
yield 'my-file-1.txt', modified_at, perms, ZIP_64, my_data_iter
my_zip_iter = stream_zip(files())
Если вам нужен объект, подобный файлу, скажем, для передачи в upload_fileobj boto3, вы можете конвертировать из iterable с помощью функции преобразования:
def to_file_like_obj(iterable):
chunk = b''
offset = 0
it = iter(iterable)
def up_to_iter(size):
nonlocal chunk, offset
while size:
if offset == len(chunk):
try:
chunk = next(it)
except StopIteration:
break
else:
offset = 0
to_yield = min(size, len(chunk) - offset)
offset = offset to_yield
size -= to_yield
yield chunk[offset - to_yield:offset]
class FileLikeObj:
def read(self, size=-1):
return b''.join(up_to_iter(float('inf') if size is None or size < 0 else size))
return FileLikeObj()
my_file_like_obj = to_file_like_obj(my_zip_iter)
Комментарии:
1. Спасибо, что написали эту библиотеку, она очень полезная и именно то, что я искал!
Ответ №2:
Возможно, вы захотите попробовать zipstream версию zipfile. Например, сжать stdin в stdout в виде zip-файла, содержащего данные в виде файла с именем TheLogFile
, используя итераторы:
#!/usr/bin/python3
import sys, zipstream
with zipstream.ZipFile(mode='w', compression=zipstream.ZIP_DEFLATED) as z:
z.write_iter('TheLogFile', sys.stdin.buffer)
for chunk in z:
sys.stdout.buffer.write(chunk)
Комментарии:
1. Ключ здесь «хранение данных в виде файла». Я не хочу использовать файл из-за ограничений среды. Как же тогда это должно выглядеть?
2. Я не совсем ясно выразился. Я просто имел в виду, что конечный результат — это поток, который, если бы вы сохранили его в файл, выглядел бы как zip-файл. Если бы вы разархивировали его, вы бы получили файл с именем
TheLogFile
, содержащий любые данные, которые вы прочитали из stdin. Единственным файлом является номинальный файл, который является частью формата выходного потока. Посмотрите наwebpy
пример в конце ссылки, так как это похоже на вашу ситуацию.3. понял, спасибо вам. Другой вопрос: похоже, что отступ немного неаккуратен,
with zipstream ...
содержит толькоz.write_iter...
only илиfor chunk...
слишком?