

# Criar uma aplicação de processamento de arquivos com tecnologia sem servidor
<a name="file-processing-app"></a>

Um dos casos de uso mais comuns do Lambda é a realização de tarefas de processamento de arquivos. Por exemplo, você pode usar uma função do Lambda para criar automaticamente arquivos PDF a partir de arquivos HTML ou de imagens, ou para criar miniaturas quando um usuário carrega uma imagem.

Neste exemplo, você cria uma aplicação que criptografa automaticamente os arquivos PDF quando eles são carregados em um bucket do Amazon Simple Storage Service (Amazon S3). Para implementar essa aplicação, você cria os seguintes recursos:
+ Um bucket do S3 no qual os usuários carregam arquivos PDF
+ Uma função do Lambda em Python que lê o arquivo carregado e cria uma versão dele criptografada e protegida por senha
+ Um segundo bucket do S3 no qual o Lambda salva o arquivo criptografado

Você também cria uma política do AWS Identity and Access Management (IAM) para dar à função do Lambda permissão para realizar operações de leitura e gravação nos buckets do S3.

![\[\]](http://docs.aws.amazon.com/pt_br/lambda/latest/dg/images/ExampleApps/file_process_resources.png)


**dica**  
Se você nunca trabalhou com o Lambda antes, recomendamos que comece com o tutorial [Criar sua primeira função do Lambda](getting-started.md) antes de criar a aplicação deste exemplo.

É possível implantar a aplicação manualmente criando e configurando os recursos com o Console de gerenciamento da AWS ou a AWS Command Line Interface (AWS CLI). Também é possível implantar a aplicação usando o AWS Serverless Application Model (AWS SAM). O AWS SAM é uma ferramenta de infraestrutura como código (IaC). Usando IaC, você não cria os recursos manualmente, mas os define no código e depois os implanta automaticamente.

Se você quiser saber mais sobre como usar o Lambda com IaC antes de implantar esta aplicação de exemplo, consulte [Usar o Lambda com a infraestrutura como código (IaC)](foundation-iac.md).

## Criar os arquivos de código-fonte da função do Lambda
<a name="file-processing-app-download"></a>

Criar os seguintes arquivos em seu diretório do projeto:
+ `lambda_function.py`: o código de função em Python para a função do Lambda que realiza a criptografia de arquivos
+ `requirements.txt`: um arquivo de manifesto que define as dependências que o código de função em Python exige

Expanda as seções a seguir para visualizar o código e saber mais sobre o perfil de cada arquivo. Para criar os arquivos na máquina local, copie e cole o código abaixo ou baixe os arquivos do [repositório aws-lambda-developer-guide do GitHub](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/file-processing-python).

### Código de função em Python
<a name="file-processing-app-function-code"></a>

Copie e cole o código a seguir em um novo arquivo denominado `lambda_function.py`.

```
from pypdf import PdfReader, PdfWriter
import uuid
import os
from urllib.parse import unquote_plus
import boto3

# Create the S3 client to download and upload objects from S3
s3_client = boto3.client('s3')

def lambda_handler(event, context):
    # Iterate over the S3 event object and get the key for all uploaded files
    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = unquote_plus(record['s3']['object']['key']) # Decode the S3 object key to remove any URL-encoded characters
        download_path = f'/tmp/{uuid.uuid4()}.pdf' # Create a path in the Lambda tmp directory to save the file to 
        upload_path = f'/tmp/converted-{uuid.uuid4()}.pdf' # Create another path to save the encrypted file to
        
        # If the file is a PDF, encrypt it and upload it to the destination S3 bucket
        if key.lower().endswith('.pdf'):
            s3_client.download_file(bucket, key, download_path)
            encrypt_pdf(download_path, upload_path)
            encrypted_key = add_encrypted_suffix(key)
            s3_client.upload_file(upload_path, f'{bucket}-encrypted', encrypted_key)

# Define the function to encrypt the PDF file with a password
def encrypt_pdf(file_path, encrypted_file_path):
    reader = PdfReader(file_path)
    writer = PdfWriter()
    
    for page in reader.pages:
        writer.add_page(page)

    # Add a password to the new PDF
    writer.encrypt("my-secret-password")

    # Save the new PDF to a file
    with open(encrypted_file_path, "wb") as file:
        writer.write(file)

# Define a function to add a suffix to the original filename after encryption
def add_encrypted_suffix(original_key):
    filename, extension = original_key.rsplit('.', 1)
    return f'{filename}_encrypted.{extension}'
```

**nota**  
No código deste exemplo, uma senha para o arquivo criptografado (`my-secret-password`) é gravada permanentemente no código da função. Não inclua informações confidenciais, como senhas, no código de função, em uma aplicação em produção. Em vez disso, [crie um segredo do AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html) e [use a extensão do Lambda AWS Parameters and Secrets](with-secrets-manager.md) para recuperar suas credenciais na função do Lambda.

O código de função em python contém três funções: a [função de handler](python-handler.md), que o Lambda executa quando a função é invocada, e duas funções separadas denominadas `add_encrypted_suffix` e `encrypt_pdf`, que o handler chama para realizar criptografia de PDF.

Quando a função é invocada pelo Amazon S3, o Lambda passa um argumento *evento* no formato JSON para a função que contém os detalhes do evento que causou a invocação. Nesse caso, as informações incluem o nome do bucket do S3 e as chaves de objeto dos arquivos carregados. Para saber mais sobre o formato do objeto de evento para o Amazon S3, consulte [Processar notificações de eventos do Amazon S3 com o Lambda](with-s3.md).

A função então usa o AWS SDK para Python (Boto3) para baixar os arquivos PDF especificados no objeto de evento para o diretório de armazenamento temporário local antes de criptografá-los usando a biblioteca [https://pypi.org/project/pypdf/](https://pypi.org/project/pypdf/).

Por fim, a função usa o SDK do Boto3 para armazenar o arquivo criptografado no bucket do S3 de destino.

### Arquivo de manifesto `requirements.txt`
<a name="file-processing-app-dependencies"></a>

Copie e cole o código a seguir em um novo arquivo denominado `requirements.txt`.

```
boto3
pypdf
```

Neste exemplo, o código da função tem apenas duas dependências que não fazem parte da biblioteca Python padrão: o SDK para Python (Boto3) e o pacote `pypdf` que a função usa para realizar dos arquivos PDF.

**nota**  
Uma versão do SDK para Python (Boto3) está incluída como parte do runtime do Lambda, portanto, o código seria executado sem adicionar o Boto3 ao pacote de implantação da função. Porém, para manter total controle das dependências da função e evitar possíveis problemas de desalinhamento de versões, a prática recomendada para o Python é incluir todas as dependências da função no pacote de implantação da função. Para saber mais, consulte [Dependências de runtime em Python](python-package.md#python-package-dependencies).

## Implantar a aplicação
<a name="file-processing-app-deploy"></a>

É possível criar e implantar os recursos para a aplicação de exemplo manualmente ou usando o AWS SAM. Em um ambiente de produção, recomendamos o uso de uma ferramenta de IaC, como o AWS SAM, para implantar integralmente as aplicações de tecnologia sem servidor com rapidez, repetidas vezes, sem usar processos manuais.

### Implantar os recursos manualmente
<a name="file-processing-app-deploy-manual"></a>

Para implantar sua aplicação manualmente:
+ Crie os buckets do Amazon S3 de origem e de destino
+ Criar uma função do Lambda que criptografa um arquivo PDF e salva a versão criptografada em um bucket do S3
+ Configure um gatilho do Lambda que invoca a função quando os objetos são carregados no bucket de origem

Antes de começar, certifique-se de que o [Python](https://www.python.org/downloads/) esteja instalado na sua máquina de compilação.

#### Criar dois buckets do S3
<a name="file-processing-app-deploy-manual-create-buckets"></a>

Primeiro, crie dois buckets do S3. O primeiro bucket corresponde ao bucket de origem no qual você vai carregar as imagens. O segundo bucket é usado pelo Lambda para salvar o arquivo criptografado quando você invoca a função.

------
#### [ Console ]

**Para criar os buckets do S3 (console)**

1. Abra a página [Buckets de uso geral](https://console.aws.amazon.com/s3/buckets) do console do Amazon S3.

1. Selecione a Região da AWS mais próxima de sua localização geográfica. É possível alterar a região usando a lista suspensa na parte superior da tela.  
![\[\]](http://docs.aws.amazon.com/pt_br/lambda/latest/dg/images/console_region_select.png)

1. Selecione **Criar bucket**.

1. Em **General configuration** (Configuração geral), faça o seguinte:

   1. Em **Tipo de bucket**, certifique-se de que **Uso geral** esteja selecionado.

   1. Em **Bucket name**, insira um nome global exclusivo que atenda às [regras de nomenclatura de buckets](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html) do Amazon S3. Os nomes dos buckets podem conter apenas letras minúsculas, números, pontos (.) e hifens (-).

1. Deixe todas as outras opções com seus valores padrão e escolha **Criar bucket**.

1. Repita as etapas 1 a 4 para criar o bucket de destino. Em **Nome do bucket**, insira `amzn-s3-demo-bucket-encrypted`, em que `amzn-s3-demo-bucket` corresponde ao nome do bucket de origem que você acabou de criar.

------
#### [ AWS CLI ]

Antes de começar, certifique-se de que a [AWS CLI esteja instalada](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) em sua máquina de compilação.

**Criar os buckets do Amazon S3 (AWS CLI)**

1. Execute o comando da CLI apresentado a seguir para criar o bucket de origem. O nome escolhido para o bucket deve ser globalmente exclusivo e seguir as [regras de nomeação de bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html) para o Amazon S3. Os nomes podem conter somente letras minúsculas, números, pontos (.) e hifens (-). Em `region` e `LocationConstraint`, escolha a [Região da AWS](https://docs.aws.amazon.com/general/latest/gr/lambda-service.html) mais próxima de sua localização geográfica.

   ```
   aws s3api create-bucket --bucket amzn-s3-demo-bucket --region us-east-2 \
   --create-bucket-configuration LocationConstraint=us-east-2
   ```

   Posteriormente no tutorial, você deverá criar a função do Lambda na mesma Região da AWS em que o bucket de origem foi criado, portanto, anote a região escolhida.

1. Execute o comando apresentado a seguir para criar o bucket de destino. Para o nome do bucket, você deve usar `amzn-s3-demo-bucket-encrypted`, em que `amzn-s3-demo-bucket` é o nome do bucket de origem criado na etapa 1. Em `region` e `LocationConstraint`, escolha a mesma Região da AWS que você usou ao criar o bucket de origem.

   ```
   aws s3api create-bucket --bucket amzn-s3-demo-bucket-encrypted --region us-east-2 \
   --create-bucket-configuration LocationConstraint=us-east-2
   ```

------

#### Criar uma função de execução
<a name="file-processing-app-deploy-manual-create-execution-role"></a>

Um perfil de execução é um perfil do IAM que concede a uma função do Lambda permissão para acessar os recursos e Serviços da AWS. Para conceder à função acesso de leitura e gravação ao Amazon S3, anexe a [política gerenciada da AWS](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#aws-managed-policies) `AmazonS3FullAccess`.

------
#### [ Console ]

**Para criar uma perfil de execução e anexar a política gerenciada da `AmazonS3FullAccess` (console)**

1. Abra a página [Roles (Funções)](https://console.aws.amazon.com/iam/home/roles) no console do IAM.

1. Selecione **Create role** (Criar função).

1. Em **Trusted entity type**, selecione **AWS service**, e em **Use case**, selecione **Lambda**.

1. Escolha **Próximo**.

1. Para adicionar a política gerenciada do `AmazonS3FullAccess`, faça o seguinte:

   1. Na caixa de pesquisa **Permissions policies**, insira **AmazonS3FullAccess**.

   1. Marque a caixa de seleção ao lado da política.

   1. Escolha **Próximo**.

1. Em **Role details**, insira **LambdaS3Role** em **Role name**.

1. Selecione **Criar perfil**.

------
#### [ AWS CLI ]

**Para criar um perfil de execução e anexar a política gerenciada da `AmazonS3FullAccess` (AWS CLI)**

1. Salve o JSON a seguir em um arquivo denominado `trust-policy.json`. Essa política de confiança permite que o Lambda use as permissões do perfil ao conceder à entidade principal de serviço a permissão `lambda.amazonaws.com` para chamar a ação `AssumeRole` do AWS Security Token Service (AWS STS).  
****  

   ```
   {
     "Version":"2012-10-17",		 	 	 
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": {
           "Service": "lambda.amazonaws.com"
         },
         "Action": "sts:AssumeRole"
       }
     ]
   }
   ```

1. No diretório em que você salvou o documento de política de confiança JSON, execute o comando da CLI apresentado a seguir para criar o perfil de execução.

   ```
   aws iam create-role --role-name LambdaS3Role --assume-role-policy-document file://trust-policy.json
   ```

1. Para anexar a política gerenciada da `AmazonS3FullAccess`, execute o comando da CLI a seguir.

   ```
   aws iam attach-role-policy --role-name LambdaS3Role --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess
   ```

------

#### Crie o pacote de implantação de função
<a name="file-processing-app-deploy-manual-create-function-package"></a>

Para criar sua função, crie um *pacote de implantação* contendo o código de função e as dependências. Para essa aplicação, o código de função usa uma biblioteca separada para a criptografia dos arquivos PDF.

**Para criar o pacote de implantação**

1. Navegue até o diretório do projeto que contém os arquivos `lambda_function.py` e `requirements.txt` que você criou ou baixou anteriormente do GitHub e crie um novo diretório denominado `package`.

1. Instale as dependências especificadas no arquivo `requirements.txt` no diretório `package`executando o comando a seguir.

   ```
   pip install -r requirements.txt --target ./package/
   ```

1. Crie um arquivo .zip contendo o código da aplicação e suas dependências. No Linux ou no MacOS, execute os comandos a seguir na interface da linha de comando.

   ```
   cd package
   zip -r ../lambda_function.zip .
   cd ..
   zip lambda_function.zip lambda_function.py
   ```

    No Windows, use sua ferramenta de zip preferida para criar o arquivo `lambda_function.zip`. Certifique-se de que o arquivo `lambda_function.py` e as pastas que contêm as dependências estejam todos na raiz do arquivo .zip.

Também é possível criar seu pacote de implantação usando um ambiente virtual Python. Consulte [Trabalhar com arquivos .zip para funções do Lambda em Python](python-package.md)

#### Criar a função do Lambda
<a name="file-processing-app-deploy-manual-createfunction"></a>

Agora, você usa o pacote de implantação criado na etapa anterior para implantar a função do Lambda.

------
#### [ Console ]

**Para criar a função (console)**

Para criar a função do Lambda usando o console, primeiro é necessário criar uma função básica contendo algum código “Hello world”. Depois, você substitui esse código por seu próprio código de função carregando o arquivo .zip criado na etapa anterior.

Para garantir que a função não atinja o tempo limite ao criptografar arquivos PDF grandes, você define as configurações de memória e de tempo limite da função. Você também define o formato de log da função como JSON. É necessário configurar os logs no formato JSON ao usar o script de teste fornecido para que ele possa ler o status da invocação da função no CloudWatch Logs para confirmar seu êxito.

1. Abra a [página Funções](https://console.aws.amazon.com/lambda/home#/functions) do console do Lambda.

1. Certifique-se de estar trabalhando na mesma Região da AWS em que criou o bucket do S3. É possível alterar a região usando a lista suspensa na parte superior da tela.  
![\[\]](http://docs.aws.amazon.com/pt_br/lambda/latest/dg/images/console_region_select.png)

1. Escolha **Create function** (Criar função).

1. Escolha **Author from scratch** (Criar do zero).

1. Em **Basic information** (Informações básicas), faça o seguinte:

   1. Em **Function name** (Nome da função), insira `EncryptPDF`.

   1. Em **Runtime**, selecione **Python 3.12**.

   1. Em **Architecture** (Arquitetura), escolha **x86\$164**.

1. Anexe o perfil de execução que você criou na etapa anterior fazendo o seguinte:

   1. Expandir a seção **Alterar função de execução padrão**.

   1. Selecione **Usar perfil existente**.

   1. Em **Existing role**, selecione seu perfil (`LambdaS3Role`).

1. Escolha a opção **Criar função**.

**Fazer upload do código da função (console)**

1. No painel do **Código-fonte**, escolha **Carregar de**.

1. Escolha o **arquivo .zip**.

1. Escolha **Carregar**.

1. No seletor de arquivos, selecione o arquivo .zip e escolha **Abrir**.

1. Escolha **Salvar**.

**Para configurar a memória e o tempo limite da função (console)**

1. Selecione a guia **Configuração** para a função.

1. No painel **Configuração geral**, escolha **Editar**.

1. Defina **Memória** como 256 MB e **Tempo limite** como 15 segundos.

1. Escolha **Salvar**.

**Para configurar o formato de log (console)**

1. Selecione a guia **Configuração** para a função.

1. Na barra lateral, selecione **Ferramentas de monitoramento e operações**.

1. No painel **Configuração de registro em log**, escolha **Editar**.

1. Em **Configuração do registro em log**, selecione **JSON**.

1. Escolha **Salvar**.

------
#### [ AWS CLI ]

**Criar a função (AWS CLI)**
+ Execute o comando a seguir no diretório que contém o arquivo `lambda_function.zip`. No parâmetro `region`, substitua `us-east-2` pela região em que você criou os buckets do S3.

  ```
  aws lambda create-function --function-name EncryptPDF \
  --zip-file fileb://lambda_function.zip --handler lambda_function.lambda_handler \
  --runtime python3.12 --timeout 15 --memory-size 256 \
  --role arn:aws:iam::123456789012:role/LambdaS3Role --region us-east-2 \
  --logging-config LogFormat=JSON
  ```

------

#### Configurar um gatilho do Amazon S3 para invocar a função
<a name="file-processing-app-deploy-manual-configure-s3-trigger"></a>

Para que a função do Lambda seja executada quando você carregar uma imagem no bucket de origem, é necessário configurar um gatilho para a função. É possível configurar o acionador do Amazon S3 usando o console ou a AWS CLI.

**Importante**  
Este procedimento configura o bucket do S3 para invocar a função sempre que um objeto é criado no bucket. Certifique-se de configurar isso somente no bucket de origem. Se a função do Lambda criar objetos no mesmo bucket que a invoca, a função poderá ser [invocada continuamente em um loop](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/recursive-runaway). Isso pode resultar em cobranças inesperadas para sua Conta da AWS.

------
#### [ Console ]

**Configurar o acionador do Amazon S3 (console)**

1. Abra a [página de Funções](https://console.aws.amazon.com/lambda/home#/functions) do console do Lambda e escolha sua função (`EncryptPDF`).

1. Escolha **Add trigger**.

1. Selecione **S3**.

1. Em **Bucket**, selecione o bucket de origem.

1. Em **Tipos de eventos**, selecione **Todos os eventos de criação de objetos**.

1. Em **Invocação recursiva**, marque a caixa de seleção para confirmar que entende que não é recomendável usar o mesmo bucket do S3 para entrada e saída. Saiba mais sobre padrões de invocação recursiva no Lambda lendo [Recursive patterns that cause run-away Lambda functions](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/recursive-runaway) no Serverless Land.

1. Escolha **Adicionar**.

   Quando você cria um acionador usando o console do Lambda, o Lambda cria automaticamente uma [política baseada em recursos](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html) para conceder permissão ao serviço selecionado para invocar a função. 

------
#### [ AWS CLI ]

**Configurar o acionador do Amazon S3 (AWS CLI)**

1. Adicione uma [política baseada em recursos](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html) à sua função que permita que seu bucket de origem do Amazon S3 invoque sua função quando você adicionar um arquivo. Uma instrução de política baseada em recursos concede permissão a outros Serviços da AWS para invocar sua função. Para conceder permissão ao Amazon S3 para invocar a função, execute o comando da CLI apresentado a seguir. Certifique-se de substituir o parâmetro `source-account` por seu ID da Conta da AWS e usar o nome do bucket de origem.

   ```
   aws lambda add-permission --function-name EncryptPDF \
   --principal s3.amazonaws.com --statement-id s3invoke --action "lambda:InvokeFunction" \
   --source-arn arn:aws:s3:::amzn-s3-demo-bucket \
   --source-account 123456789012
   ```

   A política que você define com este comando permite que o Amazon S3 invoque a função somente quando uma ação ocorre em seu bucket de origem.
**nota**  
Embora os nomes de bucket do S3 sejam globalmente exclusivos, ao usar políticas baseadas em recursos, é uma prática recomendada especificar que o bucket deve pertencer à sua conta. Isso ocorre porque, se você excluir um bucket, é possível que outra Conta da AWS crie um bucket com o mesmo nome do recurso da Amazon (ARN).

1. Salve o JSON a seguir em um arquivo denominado `notification.json`. Quando aplicado ao bucket de origem, esse JSON configura o bucket para enviar uma notificação à sua função do Lambda sempre que um novo objeto é adicionado. Substitua o número da Conta da AWS e a Região da AWS no ARN da função do Lambda por seu número de conta e sua região.

   ```
   {
   "LambdaFunctionConfigurations": [
       {
         "Id": "EncryptPDFEventConfiguration",
         "LambdaFunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:EncryptPDF",
         "Events": [ "s3:ObjectCreated:Put" ]
       }
     ]
   }
   ```

1. Execute o comando da CLI apresentado a seguir para aplicar as configurações de notificação no arquivo JSON criado ao bucket de origem. Substitua `amzn-s3-demo-bucket` pelo nome do bucket de origem.

   ```
   aws s3api put-bucket-notification-configuration --bucket amzn-s3-demo-bucket \
   --notification-configuration file://notification.json
   ```

   Para saber mais sobre o comando `put-bucket-notification-configuration` e a opção `notification-configuration`, consulte [put-bucket-notification-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-notification-configuration.html) na *Referência de comandos da CLI da AWS*.

------

### Implantar recursos usando o AWS SAM
<a name="file-processing-app-deploy-sam"></a>

Antes de começar, certifique-se de que o [Docker](https://docs.docker.com/get-docker/) e [a versão mais recente do AWS SAM da CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html) estejam instalados na sua máquina de compilação.

1. No diretório do seu projeto, copie e cole o seguinte código em um arquivo chamado `template.yaml`. Substitua os nomes dos buckets de espaço reservado:
   + Para o bucket de origem, substitua `amzn-s3-demo-bucket` por qualquer nome que esteja em conformidade com as [regras de nomenclatura de buckets do S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html).
   + Para o bucket de destino, substitua `amzn-s3-demo-bucket-encrypted` por `<source-bucket-name>-encrypted`, onde `<source-bucket>` é o nome que você escolheu para o bucket de origem.

   ```
   AWSTemplateFormatVersion: '2010-09-09'
   Transform: AWS::Serverless-2016-10-31
   
   Resources:
     EncryptPDFFunction:
       Type: AWS::Serverless::Function
       Properties:
         FunctionName: EncryptPDF
         Architectures: [x86_64]
         CodeUri: ./
         Handler: lambda_function.lambda_handler
         Runtime: python3.12
         Timeout: 15
         MemorySize: 256
         LoggingConfig:
           LogFormat: JSON
         Policies:
           - AmazonS3FullAccess
         Events:
           S3Event:
             Type: S3
             Properties:
               Bucket: !Ref PDFSourceBucket
               Events: s3:ObjectCreated:*
   
     PDFSourceBucket:
       Type: AWS::S3::Bucket
       Properties:
         BucketName: amzn-s3-demo-bucket
   
     EncryptedPDFBucket:
       Type: AWS::S3::Bucket
       Properties:
         BucketName: amzn-s3-demo-bucket-encrypted
   ```

   O modelo do AWS SAM define os recursos que você cria para a aplicação. Neste exemplo, o modelo define uma função do Lambda usando o tipo `AWS::Serverless::Function` e dois buckets do S3 usando o tipo `AWS::S3::Bucket`. Os nomes dos buckets especificados no modelo são espaços reservados. Antes de implantar a aplicação usando o AWS SAM, você precisa editar o modelo para renomear os buckets com nomes globalmente exclusivos que atendam às [regras de nomenclatura de buckets do S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html). Essa etapa é explicada com mais detalhes em [Implantar recursos usando o AWS SAM](#file-processing-app-deploy-sam).

   A definição do recurso da função do Lambda configura um gatilho para a função usando a propriedade de evento `S3Event`. Esse gatilho faz com que a função seja invocada sempre que um objeto for criado no bucket de origem.

   A definição da função também especifica uma política do AWS Identity and Access Management (IAM) a ser anexada ao [perfil de execução](lambda-intro-execution-role.md) da função. A [política gerenciada da AWS](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#aws-managed-policies) `AmazonS3FullAccess` concede à função as permissões necessárias para ler e gravar objetos no Amazon S3.

1. Execute o comando a seguir no diretório em que você salvou os arquivos `template.yaml`, `lambda_function.py` e `requirements.txt`.

   ```
   sam build --use-container
   ```

   Esse comando reúne os artefatos de compilação da aplicação e os coloca no formato e no local adequados para implantá-los. Especificar a opção `--use-container` compila a função dentro de um contêiner do Docker semelhante ao Lambda. Nós o usamos aqui para que você não precise ter o Python 3.12 instalado na máquina local para que a compilação funcione.

   Durante o processo de compilação, o AWS SAM procura o código da função do Lambda no local que você especificou com a propriedade `CodeUri` no modelo. Neste caso, especificamos o diretório atual como o local (`./`).

   Se houver um arquivo `requirements.txt` presente, o AWS SAM o usará para coletar as dependências especificadas. Por padrão, o AWS SAM cria um pacote de implantação .zip com o código e as dependências da função. Também é possível escolher implantar a função como uma imagem de contêiner usando a propriedade [PackageType](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html#sam-function-packagetype).

1. Para implantar a aplicação e criar os recursos do Lambda e do Amazon S3 especificados no modelo do AWS SAM, execute o comando a seguir.

   ```
   sam deploy --guided
   ```

   Usar o sinalizador `--guided` significa que o AWS SAM mostrará instruções para guiar você no processo de implantação. Para a implantação, aceite as opções padrão pressionando Enter.

Durante o processo de implantação, o AWS SAM cria os seguintes recursos na sua Conta da AWS:
+ Uma [pilha](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-concepts.html#cfn-concepts-stacks) do CloudFormation chamada `sam-app`
+ Uma função do Lambda com o nome de `EncryptPDF`
+ Dois buckets do S3 com os nomes que você escolheu quando editou o arquivo `template.yaml` do modelo do AWS SAM
+ Um perfil de execução do IAM para sua função com o formato de nome `sam-app-EncryptPDFFunctionRole-2qGaapHFWOQ8`

Quando o AWS SAM termina de criar os recursos, você deve ver a seguinte mensagem:

```
Successfully created/updated stack - sam-app in us-east-2
```

## Testar o aplicativo
<a name="file-processing-app-test"></a>

Para testar a aplicação, carregue um arquivo PDF no bucket de origem e confirme que o Lambda cria uma versão criptografada do arquivo no bucket de destino. Neste exemplo, você pode testar isso manualmente usando o console ou a AWS CLI, ou usando o script de teste fornecido.

Para aplicações em produção, você pode usar os métodos e as técnicas de teste tradicionais, como testes de unidade, para confirmar o funcionamento correto do código da função do Lambda. A prática recomendada também é realizar testes como os do script de teste fornecido que realizam testes de integração com recursos reais baseados na nuvem. Os testes de integração na nuvem confirmam que a infraestrutura foi implantada corretamente e que os eventos fluem entre os diferentes serviços como esperado. Para saber mais, consulte [Como testar funções e aplicações com tecnologia sem servidor](testing-guide.md).

### Testar a aplicação manualmente
<a name="file-processing-app-test-manual"></a>

É possível testar a função manualmente adicionando um arquivo PDF ao bucket de origem do Amazon S3. Quando você adiciona o arquivo ao bucket de origem, a função do Lambda deve ser invocada automaticamente e uma versão criptografada do arquivo deve ser armazenada no bucket de destino.

------
#### [ Console ]

**Para testar a aplicação carregando um arquivo (console)**

1. Para carregar um arquivo PDF no bucket do S3, faça o seguinte:

   1. Abra a página [Buckets](https://console.aws.amazon.com/s3/buckets) do console do Amazon S3 e escolha o bucket de origem.

   1. Escolha **Carregar**.

   1. Escolha **Adicionar arquivos** e use o seletor de arquivo para escolher o arquivo de imagem que você deseja carregar.

   1. Selecione **Abrir** e **Carregar**.

1. Verifique se o Lambda salvou uma versão criptografada do arquivo PDF no bucket de destino fazendo o seguinte:

   1. Retorne para a página [Buckets](https://console.aws.amazon.com/s3/buckets) do console do Amazon S3 e escolha o bucket de destino.

   1. No painel **Objetos**, você deve ver um arquivo com o formato de nome `filename_encrypted.pdf` (onde `filename.pdf` era o nome do arquivo que você enviou para o bucket de origem). Para baixar o PDF criptografado, selecione o arquivo e escolha **Baixar**.

   1. Confirme que você consegue abrir o arquivo baixado com a senha que a função do Lambda usou para protegê-lo (`my-secret-password`).

------
#### [ AWS CLI ]

**Para testar a aplicação carregando um arquivo (AWS CLI)**

1. No diretório que contém o arquivo PDF que você deseja carregar, execute o comando da CLI a seguir. Substitua o parâmetro `--bucket` pelo nome do bucket de origem. Para os parâmetros `--key` e `--body`, use o nome de arquivo do arquivo de teste.

   ```
   aws s3api put-object --bucket amzn-s3-demo-bucket --key test.pdf --body ./test.pdf
   ```

1. Confirme que a função criou uma versão criptografada do arquivo e salvou-a no bucket do S3 de destino. Execute o comando da CLI apresentado a seguir, substituindo `amzn-s3-demo-bucket-encrypted` pelo nome do bucket de destino.

   ```
   aws s3api list-objects-v2 --bucket amzn-s3-demo-bucket-encrypted
   ```

   Se a função for executada com êxito, você visualizará uma saída semelhante à apresentada a seguir. O bucket de destino deve conter um arquivo com o formato de nome `<your_test_file>_encrypted.pdf`, onde `<your_test_file>` é o nome do arquivo que você carregou.

   ```
   {
       "Contents": [
           {
               "Key": "test_encrypted.pdf",
               "LastModified": "2023-06-07T00:15:50+00:00",
               "ETag": "\"7781a43e765a8301713f533d70968a1e\"",
               "Size": 2763,
               "StorageClass": "STANDARD"
           }
       ]
   }
   ```

1. Para baixar o arquivo que o Lambda salvou no bucket de destino, execute o comando da CLI a seguir. Substitua o parâmetro `--bucket` pelo nome do bucket de destino. Para o parâmetro `--key`, use o nome do arquivo `<your_test_file>_encrypted.pdf`, onde `<your_test_file>` é o nome do arquivo de teste que você carregou.

   ```
   aws s3api get-object --bucket amzn-s3-demo-bucket-encrypted --key test_encrypted.pdf my_encrypted_file.pdf
   ```

   Esse comando baixa o arquivo para o diretório atual e salva-o como `my_encrypted_file.pdf`.

1. Confirme que você consegue abrir o arquivo baixado com a senha que a função do Lambda usou para protegê-lo (`my-secret-password`).

------

### Testar a aplicação com o script automatizado
<a name="file-processing-app-test-auto"></a>

Crie os seguintes arquivos em seu diretório do projeto:
+ `test_pdf_encrypt.py`: um script de teste que você pode usar para testar automaticamente a aplicação
+ `pytest.ini`: um arquivo de configuração para o script de teste

Expanda as seções a seguir para visualizar o código e saber mais sobre o perfil de cada arquivo.

#### Script de teste automatizado
<a name="file-processing-app-test-script"></a>

Copie e cole o código a seguir em um novo arquivo denominado `test_pdf_encrypt.py`. Certifique-se de substituir os nomes dos buckets de espaço reservado:
+ Na função `test_source_bucket_available`, substitua `amzn-s3-demo-bucket` pelo nome do bucket de origem.
+ Na função `test_encrypted_file_in_bucket`, substitua `amzn-s3-demo-bucket-encrypted` por `source-bucket-encrypted`, onde `source-bucket>` é o nome do bucket de origem.
+ Na função `cleanup`, substitua `amzn-s3-demo-bucket` pelo nome do bucket de origem e substitua `amzn-s3-demo-bucket-encrypted` pelo nome do seu bucket de destino.

```
import boto3
import json
import pytest
import time
import os

@pytest.fixture
def lambda_client():
    return boto3.client('lambda')
    
@pytest.fixture
def s3_client():
    return boto3.client('s3')

@pytest.fixture
def logs_client():
    return boto3.client('logs')

@pytest.fixture(scope='session')
def cleanup():
    # Create a new S3 client for cleanup
    s3_client = boto3.client('s3')

    yield
    # Cleanup code will be executed after all tests have finished

    # Delete test.pdf from the source bucket
    source_bucket = 'amzn-s3-demo-bucket'
    source_file_key = 'test.pdf'
    s3_client.delete_object(Bucket=source_bucket, Key=source_file_key)
    print(f"\nDeleted {source_file_key} from {source_bucket}")

    # Delete test_encrypted.pdf from the destination bucket
    destination_bucket = 'amzn-s3-demo-bucket-encrypted'
    destination_file_key = 'test_encrypted.pdf'
    s3_client.delete_object(Bucket=destination_bucket, Key=destination_file_key)
    print(f"Deleted {destination_file_key} from {destination_bucket}")
        

@pytest.mark.order(1)
def test_source_bucket_available(s3_client):
    s3_bucket_name = 'amzn-s3-demo-bucket'
    file_name = 'test.pdf'
    file_path = os.path.join(os.path.dirname(__file__), file_name)

    file_uploaded = False
    try:
        s3_client.upload_file(file_path, s3_bucket_name, file_name)
        file_uploaded = True
    except:
        print("Error: couldn't upload file")

    assert file_uploaded, "Could not upload file to S3 bucket"

    

@pytest.mark.order(2)
def test_lambda_invoked(logs_client):

    # Wait for a few seconds to make sure the logs are available
    time.sleep(5)

    # Get the latest log stream for the specified log group
    log_streams = logs_client.describe_log_streams(
        logGroupName='/aws/lambda/EncryptPDF',
        orderBy='LastEventTime',
        descending=True,
        limit=1
    )

    latest_log_stream_name = log_streams['logStreams'][0]['logStreamName']

    # Retrieve the log events from the latest log stream
    log_events = logs_client.get_log_events(
        logGroupName='/aws/lambda/EncryptPDF',
        logStreamName=latest_log_stream_name
    )

    success_found = False
    for event in log_events['events']:
        message = json.loads(event['message'])
        status = message.get('record', {}).get('status')
        if status == 'success':
            success_found = True
            break

    assert success_found, "Lambda function execution did not report 'success' status in logs."

@pytest.mark.order(3)
def test_encrypted_file_in_bucket(s3_client):
    # Specify the destination S3 bucket and the expected converted file key
    destination_bucket = 'amzn-s3-demo-bucket-encrypted'
    converted_file_key = 'test_encrypted.pdf'

    try:
        # Attempt to retrieve the metadata of the converted file from the destination S3 bucket
        s3_client.head_object(Bucket=destination_bucket, Key=converted_file_key)
    except s3_client.exceptions.ClientError as e:
        # If the file is not found, the test will fail
        pytest.fail(f"Converted file '{converted_file_key}' not found in the destination bucket: {str(e)}")

def test_cleanup(cleanup):
    # This test uses the cleanup fixture and will be executed last
    pass
```

O script de teste automatizado executa três funções de teste para confirmar que a aplicação está funcionando corretamente:
+ O teste `test_source_bucket_available` confirma que o bucket de origem foi criado com êxito carregando nele um arquivo PDF de teste.
+ O teste `test_lambda_invoked` interroga o último fluxo de logs do CloudWatch Logs para a função para confirmar que, quando você carregou o arquivo de teste, a função do Lambda foi executada e reportada como bem-sucedida.
+ O teste `test_encrypted_file_in_bucket` confirma que o bucket de destino contém o arquivo `test_encrypted.pdf` criptografado.

Após a execução de todos esses testes, o script executa uma etapa adicional de limpeza para excluir os arquivos `test.pdf` e `test_encrypted.pdf` dos buckets de origem e de destino.

Como no modelo do AWS SAM, os nomes de bucket especificados nesse arquivo são espaços reservados. Antes de executar o teste, você precisa editar esse arquivo com os nomes reais dos buckets da aplicação. Essa etapa é explicada com mais detalhes em [Testar a aplicação com o script automatizado](#file-processing-app-test-auto)

#### Arquivo de configuração do script de teste
<a name="file-processing-app-test-config"></a>

Copie e cole o código a seguir em um novo arquivo denominado `pytest.ini`.

```
[pytest]
markers =
    order: specify test execution order
```

Isso é necessário para especificar a ordem em que os testes do script `test_pdf_encrypt.py` são executados.

Para executar os testes, faça o seguinte:

1. Certifique-se de que o módulo `pytest` esteja instalado no seu ambiente local. É possível instalar o `pytest` executando o seguinte comando:

   ```
   pip install pytest
   ```

1. Salve um arquivo PDF denominado `test.pdf` no diretório que contém os arquivos `test_pdf_encrypt.py` e `pytest.ini`.

1. Abra um terminal ou um programa shell e execute o comando a seguir no diretório que contém os arquivos de teste.

   ```
   pytest -s -v
   ```

   Quando o teste for concluído, você verá uma saída como a seguinte:

   ```
   ============================================================== test session starts =========================================================
   platform linux -- Python 3.12.2, pytest-7.2.2, pluggy-1.0.0 -- /usr/bin/python3
   cachedir: .pytest_cache
   hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/pdf_encrypt_app/.hypothesis/examples')
   Test order randomisation NOT enabled. Enable with --random-order or --random-order-bucket=<bucket_type>
   rootdir: /home/pdf_encrypt_app, configfile: pytest.ini
   plugins: anyio-3.7.1, hypothesis-6.70.0, localserver-0.7.1, random-order-1.1.0
   collected 4 items
   
   test_pdf_encrypt.py::test_source_bucket_available PASSED
   test_pdf_encrypt.py::test_lambda_invoked PASSED
   test_pdf_encrypt.py::test_encrypted_file_in_bucket PASSED
   test_pdf_encrypt.py::test_cleanup PASSED
   Deleted test.pdf from amzn-s3-demo-bucket
   Deleted test_encrypted.pdf from amzn-s3-demo-bucket-encrypted
   
   
   =============================================================== 4 passed in 7.32s ==========================================================
   ```

## Próximas etapas
<a name="file-processing-app-next-steps"></a>

Agora que você criou esta aplicação de exemplo, pode usar o código fornecido como base para criar outros tipos de aplicação de processamento de arquivos. Modifique o código no arquivo `lambda_function.py` para implementar a lógica de processamento de arquivos para seu caso de uso.

Muitos casos de uso típicos de processamento de arquivos envolvem processamento de imagens. Ao usar o Python, as bibliotecas de processamento de imagens mais populares, como a [pillow](https://pypi.org/project/pillow/), normalmente contêm componentes em C ou C\$1\$1. Para garantir que o pacote de implantação da função seja compatível com o ambiente de execução do Lambda, é importante usar o binário de distribuição do fonte correto.

Ao implantar os recursos com o AWS SAM, você precisa tomar algumas medidas adicionais para incluir a distribuição do fonte correto no pacote de implantação. Como o AWS SAM não instala dependências para uma plataforma diferente da plataforma da máquina de compilação, especificar a distribuição do fonte correto (arquivo `.whl`) no arquivo `requirements.txt` não funcionará se sua máquina de compilação usar um sistema operacional ou uma arquitetura diferente do ambiente de execução do Lambda. Em vez disso, você deve realizar um dos seguintes procedimentos:
+ Use a opção `--use-container` ao executar `sam build`. Quando você especifica essa opção, o AWS SAM baixa uma imagem de base de contêiner compatível com o ambiente de execução do Lambda e cria o pacote de implantação da função em um contêiner do Docker usando essa imagem. Para saber mais, consulte [Compilando uma função do Lambda dentro de um contêiner fornecido](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/using-sam-cli-build.html#using-sam-cli-build-options-container).
+ Compile você mesmo o pacote de implantação .zip da função usando o binário de distribuição do fonte correto e salve o arquivo.zip no diretório que especificar como `CodeUri` no AWS SAM modelo. Para saber mais sobre a compilação de pacotes de implantação .zip para Python usando distribuições binárias, consulte [Criar um pacote de implantação .zip com dependências](python-package.md#python-package-create-dependencies) e [Criar pacotes de implantação .zip com bibliotecas nativas](python-package.md#python-package-native-libraries).