代码之家  ›  专栏  ›  技术社区  ›  Rpj

如何在S3中从zip存档中提取文件

  •  27
  • Rpj  · 技术社区  · 10 年前

    我在S3中的某个位置上传了一个zip存档(比如/foo/bar.zip) 我想提取bar.zip中的值,并将其放在/foo下,而无需下载或重新上载提取的文件。我如何做到这一点,以便S3被视为一个文件系统

    5 回复  |  直到 10 年前
        1
  •  42
  •   DNA    5 年前

    S3并不是为了实现这一点而设计的;通常,您必须下载文件,处理它并上传提取的文件。

    但是,可能有几个选项:

    1. 您可以使用 s3fs FUSE (参见 article github site ). 这仍然需要下载和上传文件,但它将这些操作隐藏在文件系统接口后面。

    2. 如果您主要担心的是避免将数据从AWS下载到本地计算机,那么您当然可以将数据下载到远程 EC2 instance 在那里工作,不管有没有 s3fs 。这将数据保存在亚马逊数据中心内。

    3. 您可以使用 AWS Lambda .

    您需要创建、打包和上传一个用 node.js 以访问、解压缩和上载文件。这种处理将在AWS基础设施的后台进行,因此您不需要将任何文件下载到自己的机器上。请参见 FAQs .

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

    然而,这是一种非常复杂的避免下载的方法,只有在需要处理大量zip文件时才值得这样做!另请注意(截至2018年10月)Lambda功能限制为15分钟 maximum duration ( default timeout 是3秒),因此如果您的文件非常大,可能会耗尽时间-但由于 /tmp 限制为500MB,文件大小也受到限制。

        2
  •  2
  •   Nathan    3 年前

    如果将数据保存在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对象中,并使用标准库的 ZipFile , documentation here 。一旦将数据传递到ZipFile,就可以调用 read() 关于内容。您需要从这里为您的特定用例确定要做什么。如果存档中有多个文件,则需要处理每个文件的逻辑。我的示例假设您有一个或几个小csv文件要处理,并返回一个字典,其中文件名为关键字,值设置为文件内容。

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

        3
  •  0
  •   Steve Lihn    3 年前

    以下是使用 s3fs . 允许 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 对于不同类型的文件。

        4
  •  0
  •   x89    3 年前

    您可以使用AWS Lambda。您可以编写一个Python代码,使用boto3连接到S3。然后,您可以将文件读入缓冲区,并使用以下库解压缩它们:

    import zipfile
    import io
    
    buffer = BytesIO(zipped_file.get()["Body"].read())
    zipped = zipfile.ZipFile(buffer)
    for file in zipped.namelist():
    ....
    

    这里还有一个教程: https://betterprogramming.pub/unzip-and-gzip-incoming-s3-files-with-aws-lambda-f7bccf0099c9

        5
  •  0
  •   nejckorasa    2 年前

    我遇到了一个类似的问题,并通过使用Java AWS SDK解决了它。您仍然可以下载文件并将其重新上传到S3,但关键是“流式传输”内容,而无需将任何数据保存在内存中或写入磁盘。

    我制作了一个可用于此目的的库,可在 Maven Central ,这里是GitHub链接: nejckorasa/s3-stream-unzip .

    解压缩无需将数据保存在内存中或写入磁盘即可实现。这使得它适合于大型数据文件-它已用于解压缩大小为100GB以上的文件。

    您可以将其集成到Lambda或任何可以访问S3的地方。