Как gzip при загрузке в s3 с помощью boto
У меня есть большой локальный файл. Я хочу загрузить gzipped версию этого файла в S3, используя библиотеку boto
. Файл слишком большой, чтобы эффективно загружать его на диск перед загрузкой, поэтому при загрузке он должен быть gzip потоковым способом.
Библиотека boto
знает функцию set_contents_from_file()
, которая ожидает файл-подобный объект, который он будет читать.
Библиотека gzip
знает класс GzipFile
, который может получить объект через параметр с именем fileobj
; он будет записывать этот объект при сжатии.
Я хотел бы объединить эти две функции, но один API хочет читать сам по себе, другой API хочет писать сам по себе; не знает пассивной операции (например, записывается или читается).
Есть ли у кого-нибудь идея о том, как объединить их в рабочем режиме?
EDIT: я принял один ответ (см. ниже), потому что он намекнул мне, куда идти, но если у вас есть такая же проблема, вы можете найти мой собственный ответ (также ниже) более полезным, потому что я реализовал решение с использованием multipart загружает в нее.
Ответы
Ответ 1
На самом деле это не так, потому что S3 не поддерживает истинный потоковый ввод (т.е. закодированное кодирование передачи). Вы должны знать Content-Length перед загрузкой и единственный способ узнать, что нужно выполнить операцию gzip в первую очередь.
Ответ 2
Я реализовал решение, намеченное в комментариях принятого ответа garnaat:
import cStringIO
import gzip
def sendFileGz(bucket, key, fileName, suffix='.gz'):
key += suffix
mpu = bucket.initiate_multipart_upload(key)
stream = cStringIO.StringIO()
compressor = gzip.GzipFile(fileobj=stream, mode='w')
def uploadPart(partCount=[0]):
partCount[0] += 1
stream.seek(0)
mpu.upload_part_from_file(stream, partCount[0])
stream.seek(0)
stream.truncate()
with file(fileName) as inputFile:
while True: # until EOF
chunk = inputFile.read(8192)
if not chunk: # EOF?
compressor.close()
uploadPart()
mpu.complete_upload()
break
compressor.write(chunk)
if stream.tell() > 10<<20: # min size for multipart upload is 5242880
uploadPart()
Кажется, что это работает без проблем. И в конце концов, потоковая передача в большинстве случаев просто фрагментирует данные. В этом случае куски размером около 10 МБ, но кому это нужно? Пока мы не говорим о нескольких блоках GB, я в порядке с этим.
Обновление для Python 3:
from io import BytesIO
import gzip
def sendFileGz(bucket, key, fileName, suffix='.gz'):
key += suffix
mpu = bucket.initiate_multipart_upload(key)
stream = BytesIO()
compressor = gzip.GzipFile(fileobj=stream, mode='w')
def uploadPart(partCount=[0]):
partCount[0] += 1
stream.seek(0)
mpu.upload_part_from_file(stream, partCount[0])
stream.seek(0)
stream.truncate()
with open(fileName, "rb") as inputFile:
while True: # until EOF
chunk = inputFile.read(8192)
if not chunk: # EOF?
compressor.close()
uploadPart()
mpu.complete_upload()
break
compressor.write(chunk)
if stream.tell() > 10<<20: # min size for multipart upload is 5242880
uploadPart()
Ответ 3
Вы также можете легко сжать байты с помощью gzip и легко загрузить их следующим образом:
import gzip
import boto3
cred = boto3.Session().get_credentials()
s3client = boto3.client('s3',
aws_access_key_id=cred.access_key,
aws_secret_access_key=cred.secret_key,
aws_session_token=cred.token
)
bucketname = 'my-bucket-name'
key = 'filename.gz'
s_in = b"Lots of content here"
gzip_object = gzip.compress(s_in)
s3client.put_object(Bucket=bucket, Body=gzip_object, Key=key)
Можно заменить s_in
любыми байтами, io.BytesIO, дампами, файлами и т.д.
Если вы хотите загрузить сжатый Json, вот хороший пример: Загрузить сжатый Json в S3