【问题标题】:AWS Lambda: How to extract a tgz file in a S3 bucket and put it in another S3 bucketAWS Lambda:如何在 S3 存储桶中提取 tgz 文件并将其放入另一个 S3 存储桶中
【发布时间】:2016-05-15 14:05:08
【问题描述】:

我有一个名为“Source”的 S3 存储桶。许多“.tgz”文件被实时推送到该存储桶中。我编写了一个 Java 代码来提取“.tgz”文件并将其推送到“目标”存储桶中。我将我的代码推送为 Lambda 函数。我在我的 Java 代码中获得了作为 InputStream 的“.tgz”文件。如何在 Lambda 中提取它?我无法在 Lambda 中创建文件,它会在 JAVA 中抛出“FileNotFound(Permission Denied)”。

AmazonS3 s3Client = new AmazonS3Client();
S3Object s3Object = s3Client.getObject(new GetObjectRequest(srcBucket, srcKey));
InputStream objectData = s3Object.getObjectContent();
File file = new File(s3Object.getKey());
OutputStream writer = new BufferedOutputStream(new FileOutputStream(file)); <--- It throws FileNotFound(Permission denied) here

【问题讨论】:

    标签: java amazon-web-services amazon-s3 aws-lambda


    【解决方案1】:

    由于其中一个响应是用 Python 编写的,因此我提供了这种语言的替代解决方案。

    使用 /tmp 文件系统的解决方案的问题是,AWS 只允许在其中存储 512 MB (read more)。为了解压或解压较大的文件,最好使用 io 包和BytesIO 类并纯粹在内存中处理文件内容。 AWS 允许为 Lambda 分配高达 3GB 的 RAM,这显着扩展了最大文件大小。我成功测试了 1GB S3 文件的解压。

    在我的例子中,将 ~2000 个文件从 1GB tar 文件解压缩到另一个 S3 存储桶需要 140 秒。它可以通过利用多个线程将未解压缩的文件上传到目标 S3 存储桶来进一步优化。

    下面的示例代码展示了单线程解决方案:

    import boto3
    import botocore
    import tarfile
    
    from io import BytesIO
    s3_client = boto3.client('s3')
    
    def untar_s3_file(event, context):
    
        bucket = event['Records'][0]['s3']['bucket']['name']
        key = event['Records'][0]['s3']['object']['key']
    
        input_tar_file = s3_client.get_object(Bucket = bucket, Key = key)
        input_tar_content = input_tar_file['Body'].read()
    
        with tarfile.open(fileobj = BytesIO(input_tar_content)) as tar:
            for tar_resource in tar:
                if (tar_resource.isfile()):
                    inner_file_bytes = tar.extractfile(tar_resource).read()
                    s3_client.upload_fileobj(BytesIO(inner_file_bytes), Bucket = bucket, Key = tar_resource.name)
    

    【讨论】:

      【解决方案2】:
      import boto3
      import tarfile
      from tarfile import TarInfo
      import tempfile
      
      s3_client = boto3.client('s3')
      s3_resource=boto3.resource('s3')
      def lambda_handler(event, context):
          bucket =event['Records'][0]['s3']['bucket']['name']
          key = event['Records'][0]['s3']['object']['key']
          new_bucket='uncompressed-data' #new bucket name
          new_key=key[:-4]
          try:
              with tempfile.SpooledTemporaryFile(mode='w+t') as temp:
                  s3_client.download_fileobj(bucket,key, temp)
                  temp.seek(0)
                  tar=tarfile.open(mode="r:gz", fileobj = temp)
                  for TarInfo in tar:
                      file_save=tar.extractfile(TarInfo.name)
                      s3_client.upload_fileobj(file_save,new_bucket,new_key)
                  tar.close()
                  temp.close()
          except Exception as e:
              print(e)
              raise e
      

      使用 Python 3.6 并触发带有后缀“.tgz”的 obejctcreated(all) 事件。希望对您有所帮助。

      【讨论】:

      • 我可以使用它。当然,我会注意 512 MB 的 /tmp 存储空间,这最终将我推向了不同的解决方案。此外,请记住 upload_file 仅上传单个文件,因此如果您解压缩包含多个文件的文件夹,则必须单独上传每个文件。
      【解决方案3】:

      不要使用FileFileOutputStream,使用s3Client.putObject()。要读取 tgz 文件,您可以使用 Apache Commons Compress。示例:

      ArchiveInputStream tar = new ArchiveInputStreamFactory().
          createArchiveInputStream("tar", new GZIPInputStream(objectData));
      ArchiveEntry entry;
      while ((entry = tar.getNextEntry()) != null) {
          if (!entry.isDirectory()) {
              byte[] objectBytes = new byte[entry.getSize()];
              tar.read(objectBytes);
              ObjectMetadata metadata = new ObjectMetadata();
              metadata.setContentLength(objectBytes.length);
              metadata.setContentType("application/octet-stream");
              s3Client.putObject(destBucket, entry.getName(), 
                  new ByteArrayInputStream(objectBytes), metadata);
          }
      }
      

      【讨论】:

      • 您的建议是写回目标存储桶。但我的问题是如何在 lambda 函数中提取 tgz?
      • AWS 或 lambda 提取 tgz 并没有什么特别之处。我已经使用标准 Java 库和 Apache Commons Compress 更新了我的答案。
      • 这将导致文件末尾出现空值,顺便说一句。 tar.read(objectBytes) 将读取缓冲区中的任何内容,但不能保证读取整个文件,因此 objectBytes 最后会留下一堆空值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-14
      • 1970-01-01
      • 2021-01-31
      • 2020-09-15
      • 2020-05-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多