Source code for operators.s3_unzip

from airflow.models import BaseOperator
from airflow.utils.decorators import apply_defaults


[docs] class UnzipOperator(BaseOperator): """ Unzip files from S3 to S3. :param src_bucket: source S3 bucket name :type src_bucket: str :param src_key: source S3 object key of the zip file :type src_key: str :param dest_key: destination S3 prefix where unzipped files will be stored :type dest_key: str """ ui_color = "#FF69B4" @apply_defaults def __init__( self: object, src_bucket: str, src_key: str, dest_key: str, *args: list, **kwargs: dict, ): super().__init__( *args, **kwargs, ) self.src_bucket = src_bucket self.src_key = src_key self.dest_key = dest_key
[docs] def execute( self: object, context: dict, ) -> None: try: import boto3 import zipfile from io import BytesIO import os s3_resource = boto3.resource("s3") # Get the destination key unzip_key = ( self.dest_key if self.dest_key is not None else os.path.splitext(self.src_key.replace("zipped", "unzipped"))[0] ) self.log.info(f"Source: s3://{self.src_bucket}/{self.src_key}") self.log.info(f"Destination: s3://{self.src_bucket}/{unzip_key}") # Get the zip file from S3 zip_obj = s3_resource.Object( bucket_name=self.src_bucket, key=self.src_key, ) buffer = BytesIO(zip_obj.get()["Body"].read()) # Unzip and extract files file_count = 0 skipped_count = 0 z = zipfile.ZipFile(buffer) # Log total number of files in the zip total_files = len(z.namelist()) self.log.info(f"Found {total_files} entries in zip file") for filename in z.namelist(): # Skip directories/folders if filename.endswith("/"): skipped_count += 1 continue # Construct the full destination key path, preserving directory structure dest_file_key = os.path.join(unzip_key, filename) # Upload the file to S3 s3_resource.meta.client.upload_fileobj( z.open(filename), Bucket=self.src_bucket, Key=dest_file_key, ) file_count += 1 # Only log every 10 files to reduce log spam for large zips if file_count % 10 == 0 or file_count == 1: self.log.info( f"Extracted {file_count}/{total_files-skipped_count} files" ) self.log.info(f"Successfully extracted {file_count} files from zip") self.log.info(f"Skipped {skipped_count} directory entries") self.log.info(f"Files extracted to s3://{self.src_bucket}/{unzip_key}") except Exception as e: self.log.error(f"Error unzipping file: {e}") raise e