DEV Community

Cover image for Como subir archivos con python a AWS S3 en 10 minutos
Alexis Alvarez
Alexis Alvarez

Posted on

Como subir archivos con python a AWS S3 en 10 minutos

S3 es un servicio excelente que permite almacenar y recuperar cantidades ilimitadas de información desde cualquier parte del mundo, es una de las alternativas más atractivas si no es la mejor cuando se trata de lagos de datos y es que S3 puede almacenar cualquier tipo de archivo, tiene muchas capacidades como las de encriptación hasta seguridad para accesos en todos los niveles, puede almacenar logs hasta inclusive websites estáticas.

La unidad de almacenamiento son denominados objetos, cada objeto puede pesar hasta 5 TB.

AWS S3 almacena los objetos en un contenedor al cual se le denomina “Bucket”, el cual puede almacenar de forma ilimitada un numero de objetos. Los nombres de los buckets son únicos de forma global, es decir no hay otra cuenta de AWS que pueda tener el mismo nombre de un bucket.

Para este ejemplo usaremos el AWS SDK para Python(Boto3), algunas API Calls que tenemos son las siguientes: put_object, copy, copy_object, upload_file, upload_fileobj.

Dicho lo anterior, algunas formas para subir archivos en S3 son:

1. Subir un solo archivo usando la consola de AWS S3, desde la consola de administración es posible subir un objeto hasta los 160 GB.
alt text

2. Cargar un objeto en una sola operación usando AWS CLI: entre las operaciones diponibles estan:

  • cp -Copiar
  • mv-Mover
  • sync-Sincronizar
aws s3 cp "video2.mp4" "s3://bucket-test-uploads"
Enter fullscreen mode Exit fullscreen mode

3. Cargar un objeto en una sola operación usando los AWS SDK, REST API: con una operación PUT se puede subir un objeto hasta 5GB. En esta función abrimos el archivo en memoria e invocamos la función put_object al cual le pasamos los datos del archivo, el nombre y el bucket de destino, finalmente imprimimos la respuesta o en caso de haber una excepción capturarla.

def put_object(self, s3_client):
    try:
        with open(self.file, 'rb') as fd:
            response = s3_client.put_object(
                Bucket=self.bucket,
                Key=self.key,
                Body=fd
            )
        print(json.dumps(response, sort_keys=True, indent=4))
        print("Put Object exitoso")
        return True
    except FileNotFoundError:
        print("Archivo no encontrado")
        return False
    except Exception as e:
        print(str(e))
        return False

Enter fullscreen mode Exit fullscreen mode

4. Cargar un objeto en partes usando los AWS SDK, REST API o AWS CLI con la API multipart upload, es posible subir archivos hasta 5TB .
Hemos creado la función multipart_upload, donde iniciamos el proceso de carga y obteniendo el UploadId

def multipart_upload(file, key, bucket, chunk_size, processes):

    upload_id = start_upload(bucket, key)
    print(f'Starting upload: {upload_id}')
Enter fullscreen mode Exit fullscreen mode

Abrimos el archivo en modo lectura en memoria, y definimos una cola para paralelizar procesos, cada parte debe estar entre 5MB y 100Mb

    file_upload = open(file, 'rb')
    part_procs = []
    proc_queue = multiprocessing.Queue()
    queue_returns = []
    chunk_size = (chunk_size * 1024) * 1024
    part_num = 1
    chunk = file_upload.read(chunk_size)

Enter fullscreen mode Exit fullscreen mode

Iteramos cada 5MB y los vamos metiendo a la cola, en esta sección añadimos el método add_part, el cual es el encargado de subir una parte del archivo al S3.

    while len(chunk) > 0:
        proc = multiprocessing.Process(target=add_part, args=(
            proc_queue, chunk, bucket, key, part_num, upload_id))
        part_procs.append(proc)
        part_num += 1
        chunk = file_upload.read(chunk_size)
Enter fullscreen mode Exit fullscreen mode

Realiza agrupación de procesamiento según número de procesos simultáneos, por ejemplo, un archivo de 421MB, tendrá 85 partes de 5MB aproximadamente. Como resultado se tendrá 43 ejecuciones con 2 procesos simultáneos, en la última ejecución parte tendré 1 parte

    part_procs = [part_procs[i * processes:(i + 1) * processes]
                  for i in range((len(part_procs) + (processes - 1)) //   processes)]
Enter fullscreen mode Exit fullscreen mode

Ejecutamos cada “n” procesos en simultaneo y guardamos los resultados. Ordenamos la lista basada en los 'PartNumber' los cuales son los resultados de carga por cada parte, para finalmente completar la carga con el método end_upload

    for i in range(len(part_procs)):
        for p in part_procs[i]:
            p.start()
        for p in part_procs[i]:
            p.join()
        for p in part_procs[i]:
            queue_returns.append(proc_queue.get())

    queue_returns = sorted(queue_returns, key=lambda i: i['PartNumber'])
    response = end_upload(
        bucket, key, upload_id, queue_returns)
print(json.dumps(response, sort_keys=True, indent=4))
Enter fullscreen mode Exit fullscreen mode

Si todo cargo bien veremos lo siguientes resultados:

alt text

5. Bonus: Cargar un objeto en partes usando upload_file este método es manejado por S3TransferManager.
Esto significa que automáticamente manejará las cargas multiparte por nosotros, si es necesario. A diferencia del método put_object realiza una llamada directamente a la low-level API de S3. No maneja cargas multiparte por nosotros. Intentará enviar todo en una sola solicitud

      try:
            s3_client.upload_file(self.file, self.bucket, self.key)
            print("Subida exitosa")
            return True
        except FileNotFoundError:
            print("Archivo no encontrado")
            return False
        except Exception as e:
            print(str(e))
            return False
Enter fullscreen mode Exit fullscreen mode

Un detalle importe, como buena práctica estamos usando una variable del perfil el ambiente que tengamos a AWS:

session = boto3.Session(profile_name='aadev')
Un ejempplo para la invocacion seria el siguiente
Enter fullscreen mode Exit fullscreen mode
if __name__ == '__main__':
    s3_client = session.client('s3')
    obj = S3Uploader(r'ruta\video2.mp4',
                     "video2.mp4", "aa-dev-test123", 5, 3)
    #Carga de archivos multiparte
    multipart_upload(obj.file, obj.key, obj.bucket,
                     obj.chunk_size, obj.processes)
    #Carga de archivos con upload_file                      
    obj.upload_to_aws(s3_client)
    print(obj)
Enter fullscreen mode Exit fullscreen mode

Y eso es todo por ahora, he querido compartir algunas de las formas con las que se pueden subir archivos en s3 y a esto se nos abre una gamma de posibles casos de usos, el codigo es mejorable. Hasta la proxima!
Si deseas ver más contenido puedes verlo en mi sitio: bigdateros.com

La url del repositorio se encuentra en: https://github.com/bigdateros/AWS-S3

Discussion (0)