Как сжать при загрузке в S3 с помощью Boto

У меня есть большой локальный файл. Я хочу загрузить сжатую версию этого файла в S3, используя boto библиотека. Файл слишком велик, чтобы его можно было эффективно сжать на диске перед загрузкой, поэтому он должен быть разархивирован во время загрузки.

boto библиотека знает функцию set_contents_from_file() который ожидает файл-подобный объект, из которого это будет читать.

gzip библиотека знает класс GzipFile который может получить объект через параметр с именем fileobj; он будет писать в этот объект при сжатии.

Я хотел бы объединить эти две функции, но один API хочет читать сам, другой API хочет писать сам; ни один из них не знает пассивной операции (например, запись в нее или чтение из нее).

У кого-нибудь есть идеи о том, как объединить их в рабочем режиме?

РЕДАКТИРОВАТЬ: я принял один ответ (см. Ниже), потому что он подсказал мне, куда идти, но если у вас есть та же проблема, вы могли бы найти мой собственный ответ (также ниже) более полезным, потому что я реализовал решение с использованием многочастных загрузок в нем,

2 ответа

Решение

На самом деле нет способа сделать это, потому что S3 не поддерживает истинный потоковый ввод (то есть кодирование передачи по частям). Вы должны знать Content-Length перед загрузкой и единственный способ узнать, что сначала нужно выполнить операцию gzip.

Я реализовал решение, на которое намекают в комментариях принятый ответ от 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 МБ, но кого это волнует? Пока мы не говорим о нескольких кусках ГБ, я в порядке с этим.


Обновление для 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()

Вы также можете легко сжать байты с помощью 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, pickle dump, файлами и т. д.

Если вы хотите загрузить сжатый Json, вот хороший пример: Загрузить сжатый Json в S3

Другие вопросы по тегам