【问题标题】:How to extract files from a zip archive in S3如何从 S3 中的 zip 存档中提取文件
【发布时间】:2015-04-02 05:01:24
【问题描述】:

我在某个位置(例如 /foo/bar.zip)在 S3 中上传了一个 zip 存档 我想提取 bar.zip 中的值并将其放在 /foo 下,而无需下载或重新上传提取的文件。我该怎么做才能让 S3 被视为文件系统

【问题讨论】:

    标签: amazon-web-services amazon-s3 cloud


    【解决方案1】:

    S3 并没有真正设计为允许这样做;通常你必须下载文件,处理它并上传提取的文件。

    但是,可能有几个选项:

    1. 您可以使用s3fsFUSE 将S3 存储桶挂载为本地文件系统(请参阅articlegithub site)。这仍然需要下载和上传文件,但它将这些操作隐藏在文件系统接口后面。

    2. 1234563这会将数据保留在 Amazon 数据中心内。
    3. 您可以使用AWS Lambda 对文件执行远程操作,而无需将它们下载到本地计算机上。

    您需要创建、打包和上传一个用node.js 编写的小程序来访问、解压缩和上传文件。此处理将在幕后的 AWS 基础设施上进行,因此您无需将任何文件下载到您自己的机器上。请参阅FAQs

    最后,您需要找到一种方法来触发此代码 - 通常,在 Lambda 中,这将通过将 zip 文件上传到 S3 来自动触发。如果文件已经存在,您可能需要通过 AWS API 提供的invoke-async 命令手动触发它。请参阅 AWS Lambda walkthroughsAPI docs

    但是,这是一种避免下载的相当复杂的方法,并且可能仅在您需要处理大量 zip 文件时才值得!另请注意(截至 2018 年 10 月)Lambda 函数限制为 15 分钟 maximum durationdefault timeout 为 3 秒),因此如果您的文件非常大,可能会超时 - 但由于 /tmp 中的暂存空间是限制为 500MB,您的文件大小也受到限制。

    【讨论】:

    • 现在 AWS Lambda 的执行时间限制为 5 分钟(根据serifandsemaphore.io/…)。不过值得一提。
    • /tmp 在 Lambda 中的暂存空间也只有 500mb (perhttps://aws.amazon.com/lambda/faqs/)
    • 我被要求提供有关 ideveloper 文章的登录详细信息 - 这应该发生吗?
    • 我也明白了。如果您在要求登录时点击取消,则该页面在谷歌的缓存中可用...
    • 截至 2020 年,AWS Lambda 的最长运行时间为 15 分钟。
    【解决方案2】:

    如果将数据保存在 AWS 中是目标,您可以使用 AWS Lambda 来:

    1. 连接到 S3(我通过 S3 的触发器连接 Lambda 函数)
    2. 从 S3 复制数据
    3. 打开存档并解压(无需写入磁盘)
    4. 用数据做点什么

    如果函数是通过触发器启动的,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 对象中并使用标准库的ZipFiledocumentation here 打开它。将数据传递给 ZipFile 后,您可以在内容上调用 read()。您需要从这里找出针对您的特定用例的操作。如果档案中有多个文件,您将需要处理每个文件的逻辑。我的示例假设您有一个或几个小的 csv 文件要处理并返回一个字典,其中文件名作为键,值设置为文件内容。

    我已经在响应中包含了读取 CSV 文件并返回数据和状态代码 200 的下一步。请记住,您的需求可能会有所不同。此示例将数据包装在 StringIO 对象中,并使用 CSV reader 处理数据。通过响应传递结果后,Lambda 函数可以将处理移交给另一个 AWS 进程。

    【讨论】:

      【解决方案3】:

      以下是使用 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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多