【发布时间】:2015-04-02 05:01:24
【问题描述】:
我在某个位置(例如 /foo/bar.zip)在 S3 中上传了一个 zip 存档 我想提取 bar.zip 中的值并将其放在 /foo 下,而无需下载或重新上传提取的文件。我该怎么做才能让 S3 被视为文件系统
【问题讨论】:
标签: amazon-web-services amazon-s3 cloud
我在某个位置(例如 /foo/bar.zip)在 S3 中上传了一个 zip 存档 我想提取 bar.zip 中的值并将其放在 /foo 下,而无需下载或重新上传提取的文件。我该怎么做才能让 S3 被视为文件系统
【问题讨论】:
标签: amazon-web-services amazon-s3 cloud
S3 并没有真正设计为允许这样做;通常你必须下载文件,处理它并上传提取的文件。
但是,可能有几个选项:
您可以使用s3fs 和FUSE 将S3 存储桶挂载为本地文件系统(请参阅article 和github site)。这仍然需要下载和上传文件,但它将这些操作隐藏在文件系统接口后面。
您可以使用AWS Lambda 对文件执行远程操作,而无需将它们下载到本地计算机上。
您需要创建、打包和上传一个用node.js 编写的小程序来访问、解压缩和上传文件。此处理将在幕后的 AWS 基础设施上进行,因此您无需将任何文件下载到您自己的机器上。请参阅FAQs。
最后,您需要找到一种方法来触发此代码 - 通常,在 Lambda 中,这将通过将 zip 文件上传到 S3 来自动触发。如果文件已经存在,您可能需要通过 AWS API 提供的invoke-async 命令手动触发它。请参阅 AWS Lambda walkthroughs 和 API docs。
但是,这是一种避免下载的相当复杂的方法,并且可能仅在您需要处理大量 zip 文件时才值得!另请注意(截至 2018 年 10 月)Lambda 函数限制为 15 分钟 maximum duration(default timeout 为 3 秒),因此如果您的文件非常大,可能会超时 - 但由于 /tmp 中的暂存空间是限制为 500MB,您的文件大小也受到限制。
【讨论】:
如果将数据保存在 AWS 中是目标,您可以使用 AWS Lambda 来:
如果函数是通过触发器启动的,Lambda 会建议您将内容放在单独的 S3 位置以避免意外循环。要打开存档,对其进行处理,然后返回内容,您可以执行以下操作。
import csv, json
import os
import urllib.parse
import boto3
from zipfile import ZipFile
import io
s3 = boto3.client("s3")
def extract_zip(input_zip, file_name):
contents = input_zip.read()
input_zip = ZipFile(io.BytesIO(contents))
return {name: input_zip.read(name) for name in input_zip.namelist()}
def lambda_handler(event, context):
print("Received event: " + json.dumps(event, indent=2))
# Get the object from the event and show its content type
bucket = event["Records"][0]["s3"]["bucket"]["name"]
key = urllib.parse.unquote_plus(
event["Records"][0]["s3"]["object"]["key"], encoding="utf-8"
)
try:
bucket = event['Records'][0]['s3']['bucket']['name']
key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
response = s3.get_object(Bucket=bucket, Key=key)
# This example assumes the file to process shares the archive's name
file_name = key.split(".")[0] + ".csv"
print(f"Attempting to open {key} and read {file_name}")
print("CONTENT TYPE: " + response["ContentType"])
data = []
contents = extract_zip(response["Body"], file_name)
for k, v in contents.items():
print(v)
reader = csv.reader(io.StringIO(v.decode('utf-8')), delimiter=',')
for row in reader:
data.append(row)
return {
"statusCode": 200,
"body": data
}
except Exception as e:
print(e)
print(
"Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.".format(
key, bucket
)
)
raise e
上面的代码通过response['Body']访问文件内容,其中response是S3触发的事件。响应正文将是 StreamingBody 对象的实例,它是具有一些便利功能的 file like object。如果您正在处理大文件或未知大小的文件,请使用 read() 方法,传递 amt 参数。处理内存中的档案需要一些额外的步骤。您需要正确处理内容,因此将其包装在 BytesIO 对象中并使用标准库的ZipFile、documentation here 打开它。将数据传递给 ZipFile 后,您可以在内容上调用 read()。您需要从这里找出针对您的特定用例的操作。如果档案中有多个文件,您将需要处理每个文件的逻辑。我的示例假设您有一个或几个小的 csv 文件要处理并返回一个字典,其中文件名作为键,值设置为文件内容。
我已经在响应中包含了读取 CSV 文件并返回数据和状态代码 200 的下一步。请记住,您的需求可能会有所不同。此示例将数据包装在 StringIO 对象中,并使用 CSV reader 处理数据。通过响应传递结果后,Lambda 函数可以将处理移交给另一个 AWS 进程。
【讨论】:
以下是使用 s3fs 读取 zip 存档中文件的示例。
设s3_file_path为S3上的目标文件路径-
import s3fs
from zipfile import ZipFile
import io
s3_file_path = '...'
fs = s3fs.S3FileSystem(anon=False)
input_zip = ZipFile(io.BytesIO(fs.cat(s3_file_path)))
encoding = 'ISO-8859-1' # or 'utf-8'
for name in input_zip.namelist():
data = input_zip.read(name).decode(encoding)
print("filename: " + name)
print("sample data: " + data[0:100])
您需要针对不同类型的文件调整encoding。
【讨论】: