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

Posted

技术标签:

【中文标题】AWS Lambda (Python) 无法在 S3 中解压缩和存储文件【英文标题】:AWS Lambda (Python) Fails to unzip and store files in S3 【发布时间】:2018-10-22 02:03:44 【问题描述】:

Project 当前维护 S3 存储桶,该存储桶包含 1.5 GB 的大 zip 大小,其中包含 .xpt 和 .sas7dbat 文件。解压后的文件大小为 20 GB。

尝试解压缩文件并将相同的文件夹结构推送到 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

我想验证:

    AWS-Lambda 不是大文件的合适解决方案? 我应该使用不同的库/方法而不是读取内存中的所有内容

【问题讨论】:

【参考方案1】:

有一个使用 AWS Glue 的无服务器解决方案!(我差点想死了)

此解决方案分为两部分:

    上传 ZIP 文件时由 S3 触发并创建 GlueJobRun 的 lambda 函数 - 将 S3 对象键作为参数传递给 Glue。 解压缩文件(在内存中!)并上传回 S3 的胶水作业。

请参阅下面的代码,该代码解压缩 ZIP 文件并将内容放回同一存储桶(可配置)。

如果有帮助请点赞:)

调用名为 YourGlueJob 的 Glue 作业的 Lambda 脚本 (python3)

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()

【讨论】:

你能解释一下如何使用胶水作业吗? @bruvio 胶水作业应该和上面的代码一样。在这种情况下,作业由 S3 事件触发器创建,该触发器将 S3 存储桶和密钥名称传递给函数。然后我使用 python boto 库去获取文件并处理它。【参考方案2】:

如AWS Lambda Limits link中所述:

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

在这里,您遇到的问题是因为需要“每次调用分配的内存量 Lambda 函数”。不幸的是,Lambda 不是这种情况下的适用解决方案。您需要使用 EC2 方法。

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

【讨论】:

【参考方案3】:

感谢 @Ganondorfz 的无服务器解决方案。

我尝试了类似的方法并使用 Go lambda 进行解压缩。认为在开始研究此问题时,可能值得注意的是我最初不太清楚的地方。

回答问题:

    AWS-Lambda 不是大文件的合适解决方案?

不适用于 zip 文件解压缩。 Zips 是一种存档格式,其末尾有文件索引,AFAICT 所有实用程序和库都希望在内部寻找给定的文件位置,因此受到 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

【讨论】:

以上是关于AWS Lambda (Python) 无法在 S3 中解压缩和存储文件的主要内容,如果未能解决你的问题,请参考以下文章

AWS Lambda Python 3.7:无法导入模块'lambda_function':缺少必需的依赖项['numpy']

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

如何在 Python 中从 AWS 中的 lambda 函数返回二进制数据?

AWS Lambda Python3.7 函数 - numpy:无法导入名称“WinDLL”

AWS Lambda python 错误:Runtime.ImportModuleError:无法导入模块“app”:无法从“pyparsing”导入名称“operatorPrecedence”

AWS Rekognition和s3调用Python Lambda中的子文件夹