Usar o Amazon S3 Object Lambda para marcar imagens dinamicamente à medida que elas são recuperadas

TUTORIAL

Visão geral

Com o Amazon S3 Object Lambda, você pode adicionar seu próprio código às solicitações GET, LIST e HEAD do S3 para modificar dados conforme eles são retornados a uma aplicação. Você pode usar o código personalizado para modificar os dados retornados pelas solicitações GET do S3 para converter formatos de dados (por exemplo, XML em JSON), redimensionar imagens dinamicamente, editar dados confidenciais e muito mais. Agora, você também pode usar o S3 Object Lambda para modificar a saída de solicitações LIST do S3 para criar uma visualização personalizada dos objetos em um bucket e solicitações HEAD do S3 para modificar metadados de objetos, como o nome e o tamanho do objeto.

O objetivo deste tutorial é mostrar como começar a usar o Amazon S3 Object Lambda. Muitas organizações armazenam imagens no Amazon S3 que são acessadas por diferentes aplicações, cada uma com requisitos exclusivos de formato de dados. Em certos casos, essas imagens talvez precisem ser modificadas para incluir uma marca d'água, dependendo do usuário que as acessa (por exemplo, um assinante pagante pode ver imagens sem marcas d'água, enquanto um usuário não pagante recebe uma imagem com marca d'água).

Neste tutorial, usaremos o S3 Object Lambda para adicionar uma marca d'água a uma imagem à medida que ela é recuperada do Amazon S3. O S3 Object Lambda pode ser usado para modificar dados à medida que eles são recuperados do Amazon S3, sem alterar o objeto existente ou manter várias cópias derivadas dos dados. Ao apresentar várias exibições dos mesmos dados e eliminar a necessidade de armazenar cópias derivadas, você pode economizar em custos de armazenamento.

O que você aprenderá

Neste tutorial, você vai:

  • Criar um bucket do Amazon S3
  • Criar um ponto de acesso do S3
  • Criar uma função do AWS Lambda para modificar imagens
  • Criar um ponto de acesso do S3 Object Lambda

Pré-requisitos

Para concluir este tutorial, você precisa de uma conta da AWS. Acesse esta página de suporte para obter mais informações sobre como criar e ativar uma nova conta da AWS.

Você pode criar um usuário do IAM para o tutorial ou adicionar permissões a um usuário existente do IAM. Para concluir este tutorial, seu usuário do IAM deve incluir as seguintes permissões para acessar recursos relevantes da AWS e realizar ações 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 limpar os recursos criados neste tutorial, você precisará das seguintes permissões do IAM:

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

 

 Experiência com a AWS

Iniciante

 Tempo para conclusão

20 minutos

 Custo da conclusão

 Requisitos

Conta da AWS*

*Talvez as contas criadas nas últimas 24 horas ainda não tenham acesso aos recursos exigidos para este tutorial.

 Serviços usados

 Data da última atualização

1º de fevereiro de 2023

Pré-requisitos

Para concluir este tutorial, você precisa de uma conta da AWS. Acesse esta página de suporte para obter mais informações sobre como criar e ativar uma nova conta da AWS.

Você pode criar um usuário do IAM para o tutorial ou adicionar permissões a um usuário existente do IAM. Para concluir este tutorial, seu usuário do IAM deve incluir as seguintes permissões para acessar recursos relevantes da AWS e realizar ações específicas: 

Implementação

Etapa 1: criar um bucket do Amazon S3

1.1 - Fazer login no console do Amazon S3

1.2 - Criar um bucket do S3

  • Selecione Buckets no menu do Amazon S3 no painel de navegação esquerdo e, em seguida, clique em Create bucket (Criar bucket).

1.3

  • No campo Bucket name (Nome do bucket), insira um nome descritivo e globalmente exclusivo para o seu bucket. Selecione em qual AWS Region (Região da AWS) você deseja que seu bucket seja criado. Criaremos outro recurso mais adiante neste tutorial que deve estar na mesma região da AWS.
  • Você pode deixar as seleções padrão para as opções restantes. Navegue até a parte inferior da página e escolha Create bucket (Criar bucket).

Etapa 2: Fazer upload de um objeto

Agora que seu bucket foi criado e configurado, você está pronto para carregar uma imagem.

2.1 - Carregar um objeto

  • Na lista de buckets disponíveis, selecione o nome do bucket que você acabou de criar.

2.2

  • Em seguida, verifique se a guia Objects (Objetos) está selecionada. Em seguida, na seção Objects (Objetos), selecione o botão Upload (Carregar).

2.3 - Adicionar arquivos

  • Clique em Add files (Adicionar arquivos ) e selecione o arquivo que você deseja carregar do seu navegador de arquivos.
  • Se quiser, é possível carregar essa imagem de amostra.

2.4 - Carregar

  • Navegue até a página e clique em Carregar.

2.5

  • Após o upload bem-sucedido, clique em Close (Fechar).

Etapa 3: Criar um ponto de acesso do S3

Crie um ponto de acesso do Amazon S3 que será usado para oferecer suporte ao ponto de acesso do S3 Object Lambda, que criaremos mais adiante no tutorial.

3.1 - Criar um ponto de acesso do S3

  • Navegue até o console do S3 e selecione a opção de menu Access Points (Pontos de acesso) no painel de navegação esquerdo. Em seguida, clique em Create access point (Criar ponto de acesso).

3.2

  • Na seção Properties (Propriedades), insira o nome do ponto de acesso desejado em Access point name e, em Bucket name, escolha o nome do bucket que você inseriu na Etapa 1, clicando em Browse S3 (Procurar no S3). Em Network origin (Origem da rede), defina a origem da rede como Internet.

3.3

  • Mantenha todos os outros padrões como estão. Navegue até a parte inferior da página e clique em Create access point (Criar ponto de acesso).

3.4

  • O ponto de acesso do S3 agora aparecerá na lista quando você navegar até Access Points (Pontos de acesso) no painel de navegação esquerdo.

Etapa 4: Criar a função do Lambda

  • Em seguida, crie uma função do Lambda que será invocada quando solicitações GET do S3 forem feitas por meio de um ponto de acesso do S3 Object Lambda.
  • Usaremos o AWS CloudShell no Console de Gerenciamento da AWS para criar e testar o S3 Object Lambda. Você pode usar seu próprio computador ou uma instância do AWS Cloud9 para criar a solução se atender aos seguintes requisitos:
    - Versão mais recente da AWS Command Line Interface (CLI)
    - Credenciais para criar funções/camadas do AWS Lambda e perfil do IAM
    - Python 3.9
    - utilitário zip
    - utilitário jq

4.1 - Iniciar um terminal do CloudShell

Selecione o ícone do CloudShell no menu superior direito do Console de Gerenciamento da AWS.

Se uma janela de introdução do CloudShell aparecer, fique à vontade para ler o conteúdo e escolha Close (Fechar).

Uma nova guia do navegador será aberta com o terminal do CloudShell (semelhante à captura de tela a seguir):

4.2 - Preparar o CloudShell para implantar a função do Lambda

  • Execute o código a seguir no CloudShell para preparar o ambiente e implantar a camada do Lambda com o módulo Pillow. Copie e cole o código a seguir no CloudShell para instalar as dependências necessárias e implantar a função do 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

Observação: ao copiar e colar o código, o CloudShell abrirá uma janela de aviso para você confirmar se deseja colar o código de várias linhas. Selecione Paste (Colar).

Essa etapa pode levar de 10 a 15 minutos para ser concluída.

4.3 - Criar a função do Lambda

  • Baixe uma fonte TrueType que será usada pela função do Lambda para adicionar uma marca d'água a uma imagem. Copie e cole os seguintes comandos no CloudShell.
wget https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/branding/Amazon_Typefaces_Complete_Font_Set_Mar2020.zip
  • Extraia a fonte TrueType que será usada para escrever o texto com marca d'água na imagem.
unzip -oj Amazon_Typefaces_Complete_Font_Set_Mar2020.zip "Amazon_Typefaces_Complete_Font_Set_Mar2020/Ember/AmazonEmber_Rg.ttf"
  • Crie o código do Lambda que será usado para processar as solicitações do 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
  • Crie o arquivo zip do Lambda que contém o código Python e o arquivo de fonte TrueType.
zip -r9 lambda.zip lambda.py AmazonEmber_Rg.ttf
  • Crie o perfil do IAM vinculado à função do 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"}]}'
  • Anexe uma política predefinida do IAM ao perfil do IAM criado anteriormente. Essa política contém as permissões mínimas necessárias para executar a função do 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 .[])
  • Crie e carregue a função do 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

Etapa 5: Criar um ponto de acesso do S3 Object Lambda

Crie um ponto de acesso do S3 Object Lambda que será usado para acessar as imagens armazenadas no seu bucket do S3.

5.1 - Criar o ponto de acesso do S3 Object Lambda

  • Navegue até o console do S3 e escolha Object Lambda Access Points (Pontos de acesso do Object Lambda) no painel de navegação esquerdo. Clique em Create Object Lambda Access Point (Criar ponto de acesso do Object Lambda).

Na seção General (Geral), em Object Lambda Access Point name (Nome do ponto de acesso do Object Lambda), insira ol-amazon-s3-images-guide.

Certifique-se de que a região da AWS do ponto de acesso do S3 Object Lambda corresponda à região da AWS que você especificou ao criar o bucket do S3 na Etapa 1.3.

Em Supporting Access Point (Ponto de acesso de suporte), especifique o nome de recurso da Amazon (ARN) do ponto de acesso do S3 que você criou na Etapa 3.2 usando o botão Browse S3 (Procurar no S3).

Navegue para baixo até visualizar Transformation configuration (Configuração da transformação). Na lista S3 APIs (APIs do S3), selecione a opção GetObject.

Em Lambda function (Função do Lambda), especifique ol_image_processing.

Em seguida, navegue até o final da página e escolha Create Object Lambda Access Point (Criar ponto de acesso do Object Lambda).

Etapa 6: Baixar imagens do ponto de acesso do S3 Object Lambda

Depois de criarmos o ponto de acesso do S3 Object Lambda, abriremos a imagem para verificar se uma marca d'água foi adicionada corretamente durante a solicitação.

6.1 - Abrir o ponto de acesso do S3 Object Lambda

  • Retorne à lista de pontos de acesso do S3 Object Lambda escolhendo Object Lambda Access Points (Pontos de acesso do Object Lambda) no painel de navegação esquerdo do console do S3 e selecione o ponto de acesso do S3 Object Lambda que você criou na Etapa 5.1. Neste exemplo, escolhemos o Ponto de acesso do S3 Object Lambda ol-amazon-s3-images-guide.

Selecione a imagem que você carregou na Etapa 2.4 e clique em Open (Abrir).

Uma nova guia do navegador será aberta com sua imagem e uma marca d'água.
 
Todas as imagens compatíveis baixadas do Ponto de acesso do S3 Object Lambda agora incluirão o texto com marca d'água.


6.2 - Baixar a imagem transformada da AWS CLI

  • Você também pode baixar a imagem usando a AWS CLI. Para fazer isso, você precisa do nome do recurso da Amazon (ARN) do Ponto de acesso do S3 Object Lambda. No console do S3, navegue até a página Object Lambda Access Points (Pontos de acesso do S3 Object Lambda), selecione o nome do ponto de acesso do S3 Object Lambda, selecione a guia Properties (Propriedades) e escolha o ícone de cópia abaixo de Amazon Resource Name (ARN) (Nome do recurso da Amazon).

6.3 - Executar o comando da AWS CLI a partir do CloudShell

Na guia do navegador do CloudShell, insira o seguinte:

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

6.4 - Baixar a imagem no computador local

No CloudShell, escolha Actions (Ações) no canto superior direito e selecione Download file (Baixar arquivo).

Insira o nome do arquivo definido na Etapa 6.3 ao baixar a imagem do Ponto de acesso do S3 Object Lambda e escolha Download.

Agora, você pode abrir a imagem do seu computador local.

Observação: os visualizadores de imagens podem variar de acordo com o computador e o sistema operacional. Verifique com seu administrador se não tiver certeza de qual aplicação usar para abrir a imagem.

Etapa 7: Limpar recursos

Em seguida, você limpará os recursos criados neste tutorial. É uma prática recomendada excluir recursos que não estão mais em uso a fim de evitar cobranças não intencionais.

7.1 - Excluir o ponto de acesso do S3 Object Lambda

  • Navegue até o console do S3 e escolha Object Lambda Access Points (Pontos de acesso do Object Lambda) no painel de navegação esquerdo.
  • Na página Object Lambda Access Points (Pontos de acesso do Object Lambda), clique no botão de rádio à esquerda do Ponto de acesso do S3 Object Lambda que você criou na Etapa 5.1.

Escolha Delete (Excluir).

Confirme que você deseja excluir o Ponto de acesso do S3 Object Lambda, inserindo seu nome no campo de texto exibido e, em seguida, escolha Delete (Excluir).

7.2 - Excluir o ponto de acesso do S3

  • No painel de navegação esquerdo do console do S3, escolha Access Points (Pontos de acesso).
  • Navegue até o ponto de acesso do S3 que você criou na Etapa 3.1 e clique no botão de rádio ao lado do nome dele.
  • Escolha Delete (Excluir).

Confirme que você deseja excluir o ponto de acesso, inserindo seu nome no campo de texto exibido e, em seguida, escolha Delete (Excluir).

7.3 - Excluir o objeto de teste

  • Navegue até o console do S3 e selecione a opção de menu Buckets no painel de navegação esquerdo. Primeiro, você precisará excluir o objeto de teste do seu bucket de teste. Selecione o nome do bucket com o qual você estava trabalhando neste tutorial.
  • Marque a caixa de seleção à esquerda do nome do objeto de teste e clique em Delete (Excluir).
  • Na página Delete objects (Excluir objetos), verifique se você selecionou o objeto correto para excluir e insira delete (excluir) na caixa de confirmação Permanently delete objects (Excluir objetos permanentemente). Em seguida, clique em Delete objects (Excluir objetos) para continuar.
Em seguida, você verá um banner indicando se a exclusão foi bem-sucedida.

7.4 - Excluir o bucket do S3

  • Em seguida, escolha Buckets no menu do console S3 no painel de navegação esquerdo. Clique no botão de opção à esquerda do bucket de origem que você criou para este tutorial e depois clique em Delete (Excluir).

Analise a mensagem de aviso. Se desejar continuar a exclusão deste bucket, digite o nome do bucket na caixa de confirmação Delete bucket (Excluir bucket) e escolha Delete bucket (Excluir bucket).

7.5 – Excluir a função do Lambda

  • No console do AWS Lambda, no painel de navegação esquerdo, escolha Functions (Funções).
  • Marque a caixa de seleção à esquerda do nome da função que você criou na Etapa 4.3.
  • Escolha Actions (Ações) e, em seguida, Delete (Excluir). Na caixa de diálogo Delete function (Excluir função), escolha Delete (Excluir).

Conclusão

Parabéns! Você aprendeu a usar o Amazon S3 Object Lambda para adicionar dinamicamente uma marca d'água a uma imagem à medida que ela é recuperada, retornando a imagem processada ao cliente solicitante. Você pode personalizar a função do Lambda para seu caso de uso a fim de modificar os dados retornados pelas solicitações GET, HEAD e LIST do S3, com casos de uso comuns, incluindo personalização de marcas d'água usando detalhes específicos do chamador, mascaramento de dados confidenciais, filtragem de determinadas linhas de dados, aumento de dados com informações de outros bancos de dados, conversão de formatos de dados e muito mais.

Esta página foi útil?

Próximas etapas