AWS Lambda(Python)无法解压缩并将文件存储在S3中。

6

目前项目维护了一个S3存储桶,其中包含一个大小为1.5GB的大型zip文件,里面包含.xpt和.sas7dbat文件。解压后文件大小为20GB。

尝试解压文件并将相同的文件夹结构推送到S3

以下代码适用于小型zip文件,但对于大型zip文件(1.5GB)会失败:

for obj in bucket.objects.all():
    #file_name = os.path.abspath(obj.key) # get full path of files
    key = urlparse(obj.key.encode('utf8'))
    obj = client.get_object(Bucket='my-zip-bucket', Key=obj.key)

    with io.BytesIO(obj["Body"].read()) as tf:
        # rewind the file
        tf.seek(0)

        with zipfile.ZipFile(tf, mode='r') as zipf:
            for file in zipf.infolist():
                fileName = file.filename
                putFile = client.put_object(Bucket='my-un-zip-bucket-', Key=fileName, Body=zipf.read(file))
                putObjects.append(putFile)

错误:内存大小:3008 MB 最大内存使用量:3008 MB

我想要验证:

  1. AWS-Lambda不适合处理大文件?
  2. 是否应该使用不同的库/方法而不是将所有内容读入内存?
3个回答

22

有一个使用AWS Glue的无服务器解决方案!(我差点因为找不到答案而放弃了)

这个解决方案分为两个部分:

  1. 一个Lambda函数,由S3触发上传ZIP文件并创建GlueJobRun-将S3对象键传递给Glue。
  2. 一个Glue Job,可以在内存中解压缩文件并上传回S3。

请查看下面的代码,该代码可以解压缩ZIP文件并将其内容放回到同一个存储桶中(可配置)。

如果有帮助,请点赞 :)

Lambda脚本(python3),调用名为YourGlueJob的Glue Job

import boto3
import urllib.parse

glue = boto3.client('glue')

def lambda_handler(event, context):
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    print(key)    
try:
    newJobRun = glue.start_job_run(
        JobName = 'YourGlueJob',
        Arguments = {
            '--bucket':bucket,
            '--key':key,
        }
        )
    print("Successfully created unzip job")    
    return key  
except Exception as e:
    print(e)
    print('Error starting unzip job for' + key)
    raise e         

AWS Glue作业脚本以解压缩文件

import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job

## @params: [JOB_NAME]
args = getResolvedOptions(sys.argv, ['JOB_NAME','bucket','key'],)

sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init(args['JOB_NAME'], args)

import boto3
import zipfile
import io
from contextlib import closing

s3 = boto3.client('s3')
s3r = boto3.resource('s3')

bucket = args["bucket"]
key = args["key"]

obj = s3r.Object(
    bucket_name=bucket,
    key=key
)

buffer = io.BytesIO(obj.get()["Body"].read())
z = zipfile.ZipFile(buffer)
list = z.namelist()
for filerr in list:
    print(filerr)
    y=z.open(filerr)
    arcname = key + filerr
    x = io.BytesIO(y.read())
    s3.upload_fileobj(x, bucket, arcname)
    y.close()
print(list)


job.commit()

你能解释一下如何使用 Glue Job 吗? - bruvio
@bruvio 这个 glue 作业应该与上面的代码完全一样。在这种情况下,作业是由 S3 事件触发器创建的,它将 S3 存储桶和键名传递给函数。然后我使用 Python 的 boto 库去获取文件并进行处理。 - Ganondorfz
如果我想并行解压缩zip文件,即同时解压缩存档成员...该如何做?我应该如何利用Glue并行处理能力... - Vivek Puurkayastha
这个问题回答得很好,让我感到非常开心。没有什么比这更让我高兴的了。 - RushHour

1

AWS Lambda Limits link所述:

但是,AWS Lambda施加了一些限制,包括您的部署包的大小或每个调用分配给Lambda函数的内存量。

在这里,您遇到的问题是由于需要“为每个调用分配的Lambda函数内存量”而引起的。不幸的是,Lambda对于此情况并不适用。您需要采用EC2方法。

当您的总内存需求很高时,我认为Lambda不是一个很好的解决方案。我不确定指定的文件类型如何工作,但通常使用分块方法来读取/处理大文件以避免大内存需求。分块方法是否有效取决于您的业务需求。


0

向@Ganondorfz致敬,感谢他提供的无服务器解决方案。

我尝试了类似的事情,并使用Go lambda进行解压缩。当我开始研究这个问题时,有一些初始不太清楚的地方可能值得注意。

回答以下问题:

  1. AWS-Lambda不适用于大文件?

对于zip文件解压缩来说不适用。Zip是一种存档格式,其文件索引位于末尾,所有实用程序和库都期望在其中寻找给定文件位置,因此受限于lambda的磁盘和内存约束。我想可以编写一些内容以跳转到S3对象中的范围,但这将是一个相当复杂的解决方案-我没有看到过此类实用程序(虽然我可能错了),使用具有适当资源的EC2实例或容器来实现解压缩要简单得多。

但是,在此处流式传输gzip文件并因此使用lambda进行大文件解压缩是可能的。

还可以执行用例的反向操作-从S3流式传输文件读取并将其写入S3的zip。

  • 我是否应该使用不同的库/方法而不是在内存中读取所有内容
  • 我在Go运行时中取得了更好的成功和资源利用率,但如上所述,我不认为Lambda本身适用于这种用例。

    参考资料:https://dev.to/flowup/using-io-reader-io-writer-in-go-to-stream-data-3i7b


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接