繁体   English   中英

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

[英]AWS Lambda (Python) Fails to unzip and store files in S3

项目目前维护 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

我想验证:

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

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

该解决方案分为两部分:

  1. 在上传 ZIP 文件时由 S3 触发并创建 GlueJobRun 的 lambda 函数 - 将 S3 对象键作为参数传递给 Glue。
  2. 解压缩文件(在内存中!)并上传回 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()

如此AWS Lambda 限制链接中所述

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

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

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

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

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

回答问题:

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

不适用于 zip 文件解压缩。 Zips 是一种存档格式,它在末尾具有文件索引,并且 AFAICT 所有实用程序和库都希望在内部寻找给定的文件位置,因此受到 lambda 的磁盘和内存约束的限制。 我想可以写一些跳转到 S3 对象内的范围,但这将是一个非常复杂的解决方案 - 我还没有看到用于此的实用程序(尽管我可能是错的),并且使用 EC2 实例或容器要简单得多配合适当的资源来实现解压。

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

也可以执行与用例相反的操作 - 将 S3 中的文件读取流式传输到写入 S3 的 zip 中。

  1. 我应该使用不同的库/方法而不是读取内存中的所有内容吗

我在 Go 运行时取得了更大的成功/看到了更好的资源利用率,但如上所述,我不相信 lambda 本身适用于这个用例。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM