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”