使用python模块boto3
,我再说一遍,使用boto3
,而不是boto
。如何从 S3 下载文件、gzip 并重新上传到 S3,而无需将文件写入磁盘?
我正在尝试编写一个 AWS lambda 函数,用于对上传到 S3 的所有内容进行 Gzip 压缩。问题是 lambda 函数被限制为 512MB 的磁盘空间,而我的上传可能远远超过这个。
我的假设是可以使用流来做到这一点,任何帮助将不胜感激!谢谢。
[更新]
下面的代码可以正常工作。它会将 block 上传到 S3,我可以看到生成的 *.gz
文件。但是,gzip header 未正确添加。在 mac 上打开文件会导致 Error 32 - Broken Pipe
。
有趣的是,如果文件大小小于 CHUNK_SIZE
,即只有一次迭代,则文件已上传且未损坏。
有没有看到我做错了什么?
CHUNK_SIZE = 10000000
gz_buffer = io.BytesIO()
gz_stream = gzip.GzipFile(fileobj=gz_buffer, mode='wb', compresslevel=9)
obj = resource.Object(bucket, key)
body = obj.get()['Body']
try:
while True:
data = body.read(CHUNK_SIZE)
if data:
compressed_bytes = gz_stream.write(data)
if compressed_bytes < CHUNK_SIZE:
gz_stream.close()
cdata = gz_buffer.getvalue()[0:compressed_bytes]
# Upload cdata as multipart upload
# This is a little helper function that
# uses boto3 create_multipart_upload
multipart.upload(cdata)
else:
# Signal to S3 complete multipart upload
multipart.complete()
break
except Exception as e:
pass
最佳答案
我会这样做:
import gzip,io
out_buffer = io.BytesIO()
f = gzip.open(out_buffer,"wb")
obj = resource.Object(bucket, key)
body = obj.get()['Body']
while True:
read = body.read(500000)
print('reading...')
if read:
# 1.) Stream chunks to gzip
f.seek(0)
nb_bytes = f.write(read)
# 2.) Stream compressed chunks back to S3
cdata = out_buffer.getvalue()[0:nb_bytes]
# cdata now holds the compressed chunk of data
else:
break
- 使用
io.BytesIO
在内存中创建一个“假”文件 - 在其上映射一个 gzip 句柄
- 循环读取(你的代码)
- 在写入之前寻求假文件句柄的开始,这样它就不会使用太多内存(当前写入覆盖之前的迭代写入)
- 将读取的数据写入 gzip 句柄,记下写入的字节数(它们根据数据而有所不同,如果它比上一次迭代更短,则
out_buffer
不会缩小,所以我们必须知道长度 - 使用此长度对缓冲区的内容进行切片以创建压缩 block 。
请注意,在 python 2.x 中,您不能将文件对象传递给 gzip.open
,您必须创建一个 Gzip
对象,如下所示:
f = gzip.GzipFile("foo.gz","wb",fileobj=out_buffer)
关于python - Boto3 下载 gzip 并作为流上传,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42726885/