Uso de Amazon S3 Object Lambda para agregar marcas de agua dinámicas a las imágenes a medida que se recuperan

TUTORIAL

Información general

Gracias a Amazon S3 Object Lambda, puede agregar su propio código a las solicitudes GET, HEAD, LIST de S3 para modificar datos a medida que se devuelven a una aplicación. Puede utilizar códigos personalizados para modificar los datos que devuelven las solicitudes GET de S3 para convertir formatos de datos (como XML a JSON), cambiar dinámicamente el tamaño de las imágenes, redactar datos confidenciales y mucho más. También puede utilizar Amazon S3 Object Lambda para modificar los resultados de las solicitudes LIST de S3 a fin de crear una vista personalizada de los objetos en un bucket y las solicitudes HEAD de S3 para modificar los metadatos del objeto, como su nombre y tamaño.

El objetivo de este tutorial es mostrarle cómo empezar a utilizar Amazon S3 Object Lambda. Muchas organizaciones almacenan imágenes en Amazon S3 a las que acceden diferentes aplicaciones, cada una con requisitos de formato de datos únicos. En ciertos casos, es posible que sea necesario modificar las imágenes para incluir una marca de agua en función del usuario que acceda a la imagen (por ejemplo, un suscriptor que paga puede ver imágenes sin marcas de agua, mientras que un usuario que no paga recibe una imagen con marca de agua).

En este tutorial, utilizaremos Amazon S3 Object Lambda para agregar una marca de agua a una imagen según como sea recuperada de Amazon S3. Amazon S3 Object Lambda se puede utilizar para modificar los datos a medida que se van recuperando de Amazon S3, sin cambiar el objeto existente ni mantener varias copias derivadas de los datos. Al presentar varias vistas de los mismos datos y eliminar la necesidad de almacenar copias derivadas, puede ahorrar en costos de almacenamiento.

Qué logrará

En este tutorial, hará lo siguiente:

  • Crear un bucket de Amazon S3
  • Crear un punto de acceso de S3
  • Crear una función de AWS Lambda para modificar imágenes
  • Crear un punto de acceso de Amazon S3 Object Lambda

Requisitos previos

Para realizar este tutorial, necesita una cuenta de AWS. Acceda a esta página de soporte para obtener más información sobre cómo crear y habilitar una cuenta de AWS nueva.

Puede crear un usuario de IAM para el tutorial o agregar permisos a un usuario de IAM existente. Para realizar este tutorial, su usuario de IAM debe incluir los siguientes permisos para acceder a los recursos de AWS relevantes y realizar acciones específicas:

  • s3:CreateBucket
  • s3:PutObject
  • s3:GetObject
  • s3:ListBucket
  • s3:CreateAccessPoint
  • s3:CreateAccessPointForObjectLambda
  • s3-object-lambda:WriteGetObjectResponse
  • lambda:CreateFunction
  • lambda:InvokeFunction
  • iam:AttachRolePolicy
  • iam:CreateRole
  • iam:PutRolePolicy

Para eliminar los recursos que cree en este tutorial, necesitará los siguientes permisos de IAM:

  • s3:DeleteBucket
  • s3:DeleteAccessPoint
  • s3:DeleteAccessPointForObjectLambda
  • lambda:DeleteFunction
  • iam:DeleteRole

 

 Experiencia en AWS

Principiante

 Tiempo de realización

20 minutos

 Costo de realización

 Requisitos

Cuenta de AWS*

* Es posible que las cuentas que se hayan creado dentro de las últimas 24 horas aún no tengan acceso a los recursos necesarios para este tutorial.

 Servicios utilizados

 Última actualización

1 de febrero de 2023

Requisitos previos

Para realizar este tutorial, necesita una cuenta de AWS. Acceda a esta página de soporte para obtener más información sobre cómo crear y habilitar una cuenta de AWS nueva.

Puede crear un usuario de IAM para el tutorial o agregar permisos a un usuario de IAM existente. Para realizar este tutorial, su usuario de IAM debe incluir los siguientes permisos para acceder a los recursos de AWS relevantes y realizar acciones específicas: 

Implementación

Paso 1: creación de un bucket de Amazon S3

1.1: Inicio de sesión en la consola de Amazon S3

1.2: Creación de un bucket de S3

  • Seleccione Buckets en el menú de Amazon S3 que aparece en el panel de navegación de la izquierda y, a continuación, pulse el botón Crear bucket.

1.3

  • En el campo Nombre del bucket, introduzca un nombre descriptivo y globalmente único para el bucket. Seleccione en qué región de AWS desea crear el bucket. Más adelante, crearemos otro recurso en este tutorial que estará en la misma región de AWS.
  • Puede dejar las opciones restantes con las selecciones predeterminadas. Desplácese hasta la parte inferior de la página y elija la opción Crear bucket.

Paso 2: carga de un objeto

Ahora que ha creado y configurado su bucket, podrá cargar una imagen.

2.1: Carga de un objeto

  • En la lista de buckets disponibles, seleccione el nombre del bucket que acaba de crear.

2.2

  • Después, asegúrese de que la pestaña Objetos esté seleccionada. A continuación, desde la sección Objetos, pulse el botón Cargar.

2.3: Adición de archivos

  • Pulse el botón Agregar archivos y, posteriormente, seleccione la imagen que desea cargar desde el explorador de archivos.
  • Si lo desea, puede cargar esta imagen de muestra.

2.4: Carga

  • Vaya a la parte inferior de la página y pulse el botón Cargar.

2.5

  • Una vez que se haya completado exitosamente la carga, pulse el botón Cerrar.

Paso 3: creación de un punto de acceso de S3

Cree un punto de acceso de Amazon S3 que se utilizará para admitir el punto de acceso de Amazon S3 Object Lambda, que se creará más adelante en este tutorial.

3.1: Creación de un punto de acceso de S3

  • Diríjase a la consola de S3 y seleccione la opción Puntos de acceso del menú que aparece en el panel de navegación de la izquierda. A continuación, pulse el botón Crear punto de acceso.

3.2

  • En la sección Propiedades, ingrese el nombre de punto de acceso que desee y elija el nombre de bucket que introdujo en el paso 1 seleccionando el botón Explorar S3. Luego, establezca el origen de red como Internet.

3.3

  • Deje todos los demás valores predeterminados tal y como están. Desplácese hacia la parte inferior de la página y pulse el botón Crear punto de acceso.

3.4

  • El punto de acceso de S3 ahora aparecerá en la lista cuando se desplace hacia el menú Puntos de acceso que aparece en el panel de navegación de la izquierda.

Paso 4: creación de la función de Lambda

  • A continuación, cree una función de Lambda que se invocará cuando se realicen las solicitudes GET de S3 a través de un punto de acceso de Amazon S3 Object Lambda.
  • Utilizaremos AWS CloudShell desde la consola de administración de AWS para crear y verificar Amazon S3 Object Lambda. Puede utilizar su propio equipo o una instancia de AWS Cloud9 para crear la solución si cumple con los requisitos siguientes:
    - Tener la versión más reciente de AWS Command Line Interface (CLI)
    - Tener credenciales para crear funciones o capas de AWS Lambda y un rol de IAM
    - Tener Python 3.9
    - Tener utilidad zip
    - Tener utilidad jq

4.1: Lanzamiento de un terminal de CloudShell

Seleccione el ícono de CloudShell que aparece en el menú superior derecho en la consola de administración de AWS.

Si aparece una venta de presentación de CloudShell, lea el contenido y seleccione la opción Cerrar.

Se abrirá una nueva pestaña del navegador con el terminal de CloudShell (similar a la captura de pantalla que se muestra a continuación):

4.2: Preparación de CloudShell para implementar la función de Lambda

  • Ejecute el siguiente código en CloudShell para preparar el entorno e implementar la capa de Lambda con el módulo Pillow. Copie y pegue el siguiente código en CloudShell para instalar las dependencias necesarias e implementar la función de Lambda.
# Install the required libraries to build new python
sudo yum install gcc openssl-devel bzip2-devel libffi-devel -y
# Install Pyenv
curl https://pyenv.run | bash
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
source ~/.bash_profile

# Install Python version 3.9
pyenv install 3.9.13
pyenv global 3.9.13

# Build the pillow Lambda layer
mkdir python
cd python
pip install pillow -t .
cd ..
zip -r9 pillow.zip python/
aws lambda publish-layer-version \
    --layer-name Pillow \
    --description "Python Image Library" \
    --license-info "HPND" \
    --zip-file fileb://pillow.zip \
    --compatible-runtimes python3.9

Nota: Al copiar y pegar código, CloudShell abrirá una ventana de advertencia para confirmar que desea pegar código de líneas múltiples. Seleccione la opción Pegar.

Este paso puede tardar entre 10 y 15 minutos en completarse.

4.3: Creación de la función de Lambda

  • Descargue una fuente TrueType que utilizará la función de Lambda para agregar una marca de agua a una imagen. Copie y pegue los siguientes comandos en CloudShell.
wget https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/branding/Amazon_Typefaces_Complete_Font_Set_Mar2020.zip
  • Extraiga la fuente TrueType que se utilizará para escribir el texto con marca de agua en la imagen.
unzip -oj Amazon_Typefaces_Complete_Font_Set_Mar2020.zip "Amazon_Typefaces_Complete_Font_Set_Mar2020/Ember/AmazonEmber_Rg.ttf"
  • Cree el código de Lambda que se utilizará para procesar las solicitudes de Amazon S3 Object Lambda.
cat << EOF > lambda.py
import boto3
import json
import os
import logging
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont
from urllib import request
from urllib.parse import urlparse, parse_qs, unquote
from urllib.error import HTTPError
from typing import Optional

logger = logging.getLogger('S3-img-processing')
logger.addHandler(logging.StreamHandler())
logger.setLevel(getattr(logging, os.getenv('LOG_LEVEL', 'INFO')))
FILE_EXT = {
    'JPEG': ['.jpg', '.jpeg'],
    'PNG': ['.png'],
    'TIFF': ['.tif']
}
OPACITY = 64  # 0 = transparent and 255 = full solid


def get_img_encoding(file_ext: str) -> Optional[str]:
    result = None
    for key, value in FILE_EXT.items():
        if file_ext in value:
            result = key
            break
    return result


def add_watermark(img: Image, text: str) -> Image:
    font = ImageFont.truetype("AmazonEmber_Rg.ttf", 82)
    txt = Image.new('RGBA', img.size, (255, 255, 255, 0))
    if img.mode != 'RGBA':
        image = img.convert('RGBA')
    else:
        image = img

    d = ImageDraw.Draw(txt)
    # Positioning Text
    width, height = image.size
    text_width, text_height = d.textsize(text, font)
    x = width / 2 - text_width / 2
    y = height / 2 - text_height / 2
    # Applying Text
    d.text((x, y), text, fill=(255, 255, 255, OPACITY), font=font)
    # Combining Original Image with Text and Saving
    watermarked = Image.alpha_composite(image, txt)
    return watermarked


def handler(event, context) -> dict:
    logger.debug(json.dumps(event))
    object_context = event["getObjectContext"]
    # Get the presigned URL to fetch the requested original object
    # from S3
    s3_url = object_context["inputS3Url"]
    # Extract the route and request token from the input context
    request_route = object_context["outputRoute"]
    request_token = object_context["outputToken"]
    parsed_url = urlparse(event['userRequest']['url'])
    object_key = parsed_url.path
    logger.info(f'Object to retrieve: {object_key}')
    parsed_qs = parse_qs(parsed_url.query)
    for k, v in parsed_qs.items():
        parsed_qs[k][0] = unquote(v[0])

    filename = os.path.splitext(os.path.basename(object_key))
    # Get the original S3 object using the presigned URL
    req = request.Request(s3_url)
    try:
        response = request.urlopen(req)
    except HTTPError as e:
        logger.info(f'Error downloading the object. Error code: {e.code}')
        logger.exception(e.read())
        return {'status_code': e.code}

    if encoding := get_img_encoding(filename[1].lower()):
        logger.info(f'Compatible Image format found! Processing image: {"".join(filename)}')
        img = Image.open(response)
        logger.debug(f'Image format: {img.format}')
        logger.debug(f'Image mode: {img.mode}')
        logger.debug(f'Image Width: {img.width}')
        logger.debug(f'Image Height: {img.height}')

        img_result = add_watermark(img, parsed_qs.get('X-Amz-watermark', ['Watermark'])[0])
        img_bytes = BytesIO()

        if img.mode != 'RGBA':
            # Watermark added an Alpha channel that is not compatible with JPEG. We need to convert to RGB to save
            img_result = img_result.convert('RGB')
            img_result.save(img_bytes, format='JPEG')
        else:
            # Will use the original image format (PNG, GIF, TIFF, etc.)
            img_result.save(img_bytes, encoding)
        img_bytes.seek(0)
        transformed_object = img_bytes.read()

    else:
        logger.info(f'File format not compatible. Bypass file: {"".join(filename)}')
        transformed_object = response.read()

    # Write object back to S3 Object Lambda
    s3 = boto3.client('s3')
    # The WriteGetObjectResponse API sends the transformed data
    if os.getenv('AWS_EXECUTION_ENV'):
        s3.write_get_object_response(
            Body=transformed_object,
            RequestRoute=request_route,
            RequestToken=request_token)
    else:
        # Running in a local environment. Saving the file locally
        with open(f'myImage{filename[1]}', 'wb') as f:
            logger.debug(f'Writing file: myImage{filename[1]} to the local filesystem')
            f.write(transformed_object)

    # Exit the Lambda function: return the status code
    return {'status_code': 200}
EOF
  • Cree el archivo zip de Lambda que contiene el código Python y el archivo de la fuente TrueType.
zip -r9 lambda.zip lambda.py AmazonEmber_Rg.ttf
  • Cree el rol de IAM que se asocia a la función de Lambda.
aws iam create-role --role-name ol-lambda-images --assume-role-policy-document '{"Version": "2012-10-17","Statement": [{"Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'
  • Asocie una política de IAM predefinida al rol de IAM creado anteriormente. Esta política contiene los permisos mínimos necesarios para ejecutar la función de Lambda.
aws iam attach-role-policy --role-name ol-lambda-images --policy-arn arn:aws:iam::aws:policy/service-role/AmazonS3ObjectLambdaExecutionRolePolicy

export OL_LAMBDA_ROLE=$(aws iam get-role --role-name ol-lambda-images | jq -r .Role.Arn)

export LAMBDA_LAYER=$(aws lambda list-layers --query 'Layers[?contains(LayerName, `Pillow`) == `true`].LatestMatchingVersion.LayerVersionArn' | jq -r .[])
  • Cree y cargue la función de Lambda.
aws lambda create-function --function-name ol_image_processing \
 --zip-file fileb://lambda.zip --handler lambda.handler --runtime python3.9 \
 --role $OL_LAMBDA_ROLE \
 --layers $LAMBDA_LAYER \
 --memory-size 1024

Paso 5: creación de un punto de acceso de Amazon S3 Object Lambda

Cree un punto de acceso de Amazon S3 Object Lambda que se utilizará para acceder a las imágenes almacenadas en su bucket de S3.

5.1: Creación del punto de acceso de Amazon S3 Object Lambda

En la sección General , en Nombre del punto de acceso del objeto Lambda, ingrese ol-amazon-s3-images-guide.

Asegúrese de que la región de AWS para el punto de acceso de Amazon S3 Object Lambda coincida con la región de AWS que ha especificado al crear el bucket de S3 en el paso 1.3.

En Punto de acceso de apoyo, especifique el nombre de recurso de Amazon (ARN) del punto de acceso de Amazon S3 que ha creado en el paso 3.2 utilizando el botón Explorar S3.

Desplácese hacia abajo para ver Configuración de la transformación. En la lista de API de S3, seleccione la opción GetObject.

En Función de Lambda, especifique ol_image_processing.

A continuación, desplácese hacia la parte inferior de la página y elija la opción Crear punto de acceso del objeto Lambda.

Paso 6: descarga de imágenes desde el punto de acceso de S3 Object Lambda

Después de crear el punto de acceso de S3 Object Lambda, abriremos la imagen para comprobar que se ha agregado correctamente una marca de agua durante la solicitud.

6.1: Apertura del punto de acceso de S3 Object Lambda

  • Para regresar a la lista de puntos de acceso de S3 Object Lambda, seleccione la opción Puntos de acceso del objeto Lambda en el panel de navegación que aparece en la parte izquierda de la consola de S3 y, posteriormente, seleccione el punto de acceso de Amazon S3 Object Lambda que ha creado en el paso 5.1. En este ejemplo, hemos elegido el punto de acceso de S3 Object Lambda ol-amazon-s3-images-guide.

Seleccione la imagen que ha cargado en el paso 2.4 y, a continuación, pulse el botón Abrir.

Se abrirá una nueva pestaña del navegador con la imagen y una marca de agua.
 
Todas las imágenes compatibles descargadas del punto de acceso de S3 Object Lambda ahora incluirán el texto con marca de agua.


6.2: Descarga de la imagen transformada de AWS CLI

  • También puede descargar la imagen mediante AWS CLI. Para ello, necesita el nombre de recurso de Amazon (ARN) del punto de acceso de S3 Object Lambda. En la consola de S3, diríjase a la página Object Lambda Access Points, seleccione el nombre del punto de acceso de Amazon S3 Object Lambda, seleccione la pestaña Propiedades y elija el icono de copia que se encuentra debajo del nombre de recurso de Amazon (ARN).

6.3: Ejecución del comando de AWS CLI desde CloudShell

En la pestaña del navegador CloudShell, ingrese lo siguiente:

aws s3api get-object --bucket <paste the ARN copied above here> --key <image filename here> <filename to write here>

6.4: Descarga de la imagen al equipo local

En CloudShell, elija la opción Acciones que aparece en la esquina superior derecha y seleccione la opción Descargar archivo.

Ingrese el nombre de archivo que ha definido en el paso 6.3 al descargar la imagen desde el punto de acceso de Amazon S3 Object Lambda y seleccione la opción Descargar.

Ahora puede abrir la imagen desde su equipo local.

Nota: Los visores de imágenes pueden variar según el equipo y el sistema operativo. Consulte con el administrador en caso de tener dudas sobre qué aplicación utilizar para abrir la imagen.

Paso 7: eliminación de recursos

A continuación, tiene que eliminar los recursos que ha creado en este tutorial. Es una práctica recomendada eliminar los recursos que ya no se utilizan para no incurrir en cargos no deseados.

7.1: Eliminación del punto de acceso de S3 Object Lambda

  • Diríjase a la consola de S3 y elija la opción Puntos de acceso del objeto Lambda que aparece en el panel de navegación de la izquierda.
  • En la página Puntos de acceso del objeto Lambda, pulse el botón de selección situado a la izquierda del punto de acceso de Amazon S3 Object Lambda que ha creado en el paso 5.1.

Seleccione la opción Eliminar.

Confirme que desea eliminar el punto de acceso de Amazon S3 Object Lambda ingresando su nombre en el campo de texto que aparece y, a continuación, seleccione la opción Eliminar.

7.2: Eliminación del punto de acceso de S3

  • En el panel de navegación del lado izquierdo de la consola de S3, seleccione la opción Puntos de acceso.
  • Desplácese hasta el punto de acceso de S3 que ha creado en el paso 3.1 y pulse el botón de selección situado junto al nombre del punto de acceso de S3.
  • Seleccione la opción Eliminar.

Confirme que desea eliminar el punto de acceso ingresando su nombre en el campo de texto que aparece y, a continuación, seleccione la opción Eliminar.

7.3: Eliminación del objeto de prueba

  • Diríjase a la consola de S3 y seleccione la opción Buckets del menú que aparece en el panel de navegación de la izquierda. Primero, tiene que eliminar los objetos de prueba del bucket de prueba. Seleccione el nombre del bucket con el que ha estado trabajando en este tutorial.
  • Marque la casilla de verificación que está a la izquierda del nombre del objeto de prueba y, luego, pulse el botón Eliminar.
  • En la página Eliminar objetos, verifique que ha seleccionado el objeto apropiado para eliminarlo e ingrese “eliminar” en la casilla de confirmación Eliminar objetos permanentemente. Después, pulse el botón Eliminar objetos para continuar.
A continuación, verá un anuncio que le indicará si la eliminación se ha realizado correctamente.

7.4: Eliminación del bucket de S3

  • A continuación, seleccione Buckets del menú de la consola de S3 que aparece en el panel de navegación de la izquierda. Seleccione el botón de selección que está a la izquierda del bucket de origen que ha creado para este tutorial y, posteriormente, pulse el botón Eliminar.

Revise el mensaje de advertencia. Si desea continuar con la eliminación de este bucket, ingrese el nombre de este en la casilla de confirmación Eliminar bucket y elija la opción Eliminar bucket.

7.5: Eliminación de la función de Lambda

  • En la consola de AWS Lambda, elija la opción Funciones del panel de navegación de la izquierda.
  • Seleccione la casilla de verificación situada a la izquierda del nombre de la función que ha creado en el paso 4.3.
  • Elija la opción Acciones y, a continuación, seleccione Eliminar. En el cuadro de diálogo Eliminar función, seleccione la opción Eliminar.

Conclusión

¡Enhorabuena! Ha aprendido a utilizar Amazon S3 Object Lambda para agregar de forma dinámica una marca de agua a una imagen a medida que se recupera y devolver la imagen procesada al cliente solicitante. Puede personalizar la función de Lambda para su caso de uso a fin de modificar los datos que devuelven las solicitudes GET, HEAD y LIST de S3. Los casos de uso más habituales incluyen la personalización de marcas de agua con detalles específicos del peticionario, el enmascaramiento de datos confidenciales, el filtrado de determinadas filas de datos, la optimización de los datos con información de otras bases de datos, la conversión de formatos de datos y mucho más.

¿Le resultó útil esta página?

Siguientes pasos