

# Escritura de funciones de Lambda para puntos de acceso de S3 Object Lambda
<a name="olap-writing-lambda"></a>

**nota**  
A partir del 7 de noviembre de 2025, S3 Object Lambda solo estará disponible para los clientes actuales que utilizan el servicio actualmente, así como para socios seleccionados de la red de socios de AWS (APN). Para obtener más información sobre capacidades similares a las de S3 Object Lambda, haga clic aquí: [Cambio de disponibilidad de Amazon S3 Object Lambda](https://docs.aws.amazon.com/AmazonS3/latest/userguide/amazons3-ol-change.html).

En esta sección se detalla cómo escribir las funciones de AWS Lambda para usarlas con puntos de acceso de Amazon S3 Object Lambda.

Para obtener información sobre procedimientos integrales completos para algunas tareas de S3 Object Lambda, consulte:
+ [Tutorial: transformación de datos para su aplicación con S3 Object Lambda](tutorial-s3-object-lambda-uppercase.md)
+ [Tutorial: detección y redacción de datos de PII con S3 Object Lambda y Amazon Comprehend](tutorial-s3-object-lambda-redact-pii.md)
+ [Tutorial: Uso de S3 Object Lambda para agregar marcas de agua dinámicas a las imágenes a medida que se recuperan](https://aws.amazon.com/getting-started/hands-on/amazon-s3-object-lambda-to-dynamically-watermark-images/?ref=docs_gateway/amazons3/olap-writing-lambda.html)

**Topics**
+ [Trabajar con solicitudes `GetObject` en Lambda](#olap-getobject-response)
+ [Trabajar con solicitudes `HeadObject` en Lambda](#olap-headobject)
+ [Trabajar con solicitudes `ListObjects` en Lambda](#olap-listobjects)
+ [Trabajar con solicitudes `ListObjectsV2` en Lambda](#olap-listobjectsv2)
+ [Formato y uso del contexto del evento](olap-event-context.md)
+ [Trabajar con encabezados Range y partNumber](range-get-olap.md)

## Trabajar con solicitudes `GetObject` en Lambda
<a name="olap-getobject-response"></a>

En esta sección se asume que el punto de acceso de Object Lambda está configurado para llamar a la función de Lambda para `GetObject`. S3 Object Lambda incluye la operación de la API de Amazon S3, `WriteGetObjectResponse`, que le permite a la función de Lambda proporcionar datos personalizados y encabezados de respuesta al intermediario de `GetObject`. 

`WriteGetObjectResponse` proporciona un amplio control sobre el código de estado, los encabezados de respuesta y el cuerpo de respuesta, en función de sus necesidades de procesamiento. Puede utilizar `WriteGetObjectResponse` para responder con todo el objeto transformado, partes del objeto transformado u otras respuestas en función del contexto de la aplicación. En la siguiente sección se muestran ejemplos únicos del uso de la operación de la API `WriteGetObjectResponse`.
+ **Ejemplo 1:** responder con un código de estado HTTP 403 (Prohibido) 
+ **Ejemplo 2:** responder con una imagen transformada
+ **Ejemplo 3:** transmitir contenido comprimido

**Ejemplo 1: responder con un código de estado HTTP 403 (Prohibido) **

Puede usar `WriteGetObjectResponse` para responder con el código de estado HTTP 403 (Prohibido) en función del contenido del objeto.

------
#### [ Java ]



```
package com.amazon.s3.objectlambda;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.S3ObjectLambdaEvent;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest;

import java.io.ByteArrayInputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example1 {

    public void handleRequest(S3ObjectLambdaEvent event, Context context) throws Exception {
        S3Client s3Client = S3Client.builder().build();

        // Check to see if the request contains all of the necessary information.
        // If it does not, send a 4XX response and a custom error code and message.
        // Otherwise, retrieve the object from S3 and stream it
        // to the client unchanged.
        var tokenIsNotPresent = !event.getUserRequest().getHeaders().containsKey("requiredToken");
        if (tokenIsNotPresent) {
            s3Client.writeGetObjectResponse(WriteGetObjectResponseRequest.builder()
                    .requestRoute(event.outputRoute())
                    .requestToken(event.outputToken())
                    .statusCode(403)
                    .contentLength(0L)
                    .errorCode("MissingRequiredToken")
                    .errorMessage("The required token was not present in the request.")
                    .build(),
                    RequestBody.fromInputStream(new ByteArrayInputStream(new byte[0]), 0L));
            return;
        }

        // Prepare the presigned URL for use and make the request to S3.
        HttpClient httpClient = HttpClient.newBuilder().build();
        var presignedResponse = httpClient.send(
                HttpRequest.newBuilder(new URI(event.inputS3Url())).GET().build(),
                HttpResponse.BodyHandlers.ofInputStream());

        // Stream the original bytes back to the caller.
        s3Client.writeGetObjectResponse(WriteGetObjectResponseRequest.builder()
                .requestRoute(event.outputRoute())
                .requestToken(event.outputToken())
                .build(),
                RequestBody.fromInputStream(presignedResponse.body(),
                    presignedResponse.headers().firstValueAsLong("content-length").orElse(-1L)));
    }
}
```

------
#### [ Python ]



```
import boto3
import requests 

def handler(event, context):
    s3 = boto3.client('s3')

    """
    Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
    should be delivered and contains a presigned URL in 'inputS3Url' where we can download the requested object from.
    The 'userRequest' object has information related to the user who made this 'GetObject' request to 
    S3 Object Lambda.
    """
    get_context = event["getObjectContext"]
    user_request_headers = event["userRequest"]["headers"]

    route = get_context["outputRoute"]
    token = get_context["outputToken"]
    s3_url = get_context["inputS3Url"]

    # Check for the presence of a 'CustomHeader' header and deny or allow based on that header.
    is_token_present = "SuperSecretToken" in user_request_headers

    if is_token_present:
        # If the user presented our custom 'SuperSecretToken' header, we send the requested object back to the user.
        response = requests.get(s3_url)
        s3.write_get_object_response(RequestRoute=route, RequestToken=token, Body=response.content)
    else:
        # If the token is not present, we send an error back to the user. 
        s3.write_get_object_response(RequestRoute=route, RequestToken=token, StatusCode=403,
        ErrorCode="NoSuperSecretTokenFound", ErrorMessage="The request was not secret enough.")

    # Gracefully exit the Lambda function.
    return { 'status_code': 200 }
```

------
#### [ Node.js ]



```
const { S3 } = require('aws-sdk');
const axios = require('axios').default;

exports.handler = async (event) => {
    const s3 = new S3();

    // Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
    // should be delivered and contains a presigned URL in 'inputS3Url' where we can download the requested object from.
    // The 'userRequest' object has information related to the user who made this 'GetObject' request to S3 Object Lambda.
    const { userRequest, getObjectContext } = event;
    const { outputRoute, outputToken, inputS3Url } = getObjectContext;

    // Check for the presence of a 'CustomHeader' header and deny or allow based on that header.
    const isTokenPresent = Object
        .keys(userRequest.headers)
        .includes("SuperSecretToken");

    if (!isTokenPresent) {
        // If the token is not present, we send an error back to the user. The 'await' in front of the request
        // indicates that we want to wait for this request to finish sending before moving on. 
        await s3.writeGetObjectResponse({
            RequestRoute: outputRoute,
            RequestToken: outputToken,
            StatusCode: 403,
            ErrorCode: "NoSuperSecretTokenFound",
            ErrorMessage: "The request was not secret enough.",
        }).promise();
    } else {
        // If the user presented our custom 'SuperSecretToken' header, we send the requested object back to the user.
        // Again, note the presence of 'await'.
        const presignedResponse = await axios.get(inputS3Url);
        await s3.writeGetObjectResponse({
            RequestRoute: outputRoute,
            RequestToken: outputToken,
            Body: presignedResponse.data,
        }).promise();
    }

    // Gracefully exit the Lambda function.
    return { statusCode: 200 };
}
```

------

**Ejemplo 2: responder con una imagen transformada**

Cuando realice una transformación de imagen, es posible que necesite todos los bytes del objeto de origen antes de comenzar a procesarlos. En este caso, la solicitud `WriteGetObjectResponse` devolverá el objeto completo a la aplicación solicitante en una llamada.

------
#### [ Java ]



```
package com.amazon.s3.objectlambda;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.S3ObjectLambdaEvent;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.Image;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example2V2 {

    private static final int HEIGHT = 250;
    private static final int WIDTH = 250;

    public void handleRequest(S3ObjectLambdaEvent event, Context context) throws Exception {
        S3Client s3Client = S3Client.builder().build();
        HttpClient httpClient = HttpClient.newBuilder().build();

        // Prepare the presigned URL for use and make the request to S3.
        var presignedResponse = httpClient.send(
                HttpRequest.newBuilder(new URI(event.inputS3Url())).GET().build(),
                HttpResponse.BodyHandlers.ofInputStream());

        // The entire image is loaded into memory here so that we can resize it.
        // Once the resizing is completed, we write the bytes into the body
        // of the WriteGetObjectResponse request.
        var originalImage = ImageIO.read(presignedResponse.body());
        var resizingImage = originalImage.getScaledInstance(WIDTH, HEIGHT, Image.SCALE_DEFAULT);
        var resizedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        resizedImage.createGraphics().drawImage(resizingImage, 0, 0, WIDTH, HEIGHT, null);

        var baos = new ByteArrayOutputStream();
        ImageIO.write(resizedImage, "png", baos);

        // Stream the bytes back to the caller.
        s3Client.writeGetObjectResponse(WriteGetObjectResponseRequest.builder()
                .requestRoute(event.outputRoute())
                .requestToken(event.outputToken())
                .build(), RequestBody.fromBytes(baos.toByteArray()));
    }
}
```

------
#### [ Python ]



```
import boto3
import requests 
import io
from PIL import Image

def handler(event, context):
    """
    Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
    should be delivered and has a presigned URL in 'inputS3Url' where we can download the requested object from.
    The 'userRequest' object has information related to the user who made this 'GetObject' request to 
    S3 Object Lambda.
    """
    get_context = event["getObjectContext"]
    route = get_context["outputRoute"]
    token = get_context["outputToken"]
    s3_url = get_context["inputS3Url"]

    """
    In this case, we're resizing .png images that are stored in S3 and are accessible through the presigned URL
    'inputS3Url'.
    """
    image_request = requests.get(s3_url)
    image = Image.open(io.BytesIO(image_request.content))
    image.thumbnail((256,256), Image.ANTIALIAS)

    transformed = io.BytesIO()
    image.save(transformed, "png")

    # Send the resized image back to the client.
    s3 = boto3.client('s3')
    s3.write_get_object_response(Body=transformed.getvalue(), RequestRoute=route, RequestToken=token)

    # Gracefully exit the Lambda function.
    return { 'status_code': 200 }
```

------
#### [ Node.js ]



```
const { S3 } = require('aws-sdk');
const axios = require('axios').default;
const sharp = require('sharp');

exports.handler = async (event) => {
    const s3 = new S3();

    // Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
    // should be delivered and has a presigned URL in 'inputS3Url' where we can download the requested object from.
    const { getObjectContext } = event;
    const { outputRoute, outputToken, inputS3Url } = getObjectContext;

    // In this case, we're resizing .png images that are stored in S3 and are accessible through the presigned URL
    // 'inputS3Url'.
    const { data } = await axios.get(inputS3Url, { responseType: 'arraybuffer' });

    // Resize the image.
    const resized = await sharp(data)
        .resize({ width: 256, height: 256 })
        .toBuffer();

    // Send the resized image back to the client.
    await s3.writeGetObjectResponse({
        RequestRoute: outputRoute,
        RequestToken: outputToken,
        Body: resized,
    }).promise();

    // Gracefully exit the Lambda function.
    return { statusCode: 200 };
}
```

------

**Ejemplo 3: transmitir contenido comprimido**

Cuando se comprimen objetos, los datos comprimidos se producen de forma progresiva. En consecuencia, la solicitud `WriteGetObjectResponse` se puede utilizar para devolver los datos comprimidos tan pronto como estén listos. Como se muestra en el siguiente ejemplo, no es necesario conocer la duración de la transformación completada.

------
#### [ Java ]



```
package com.amazon.s3.objectlambda;

import com.amazonaws.services.lambda.runtime.events.S3ObjectLambdaEvent;
import com.amazonaws.services.lambda.runtime.Context;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example3 {

    public void handleRequest(S3ObjectLambdaEvent event, Context context) throws Exception {
        S3Client s3Client = S3Client.builder().build();
        HttpClient httpClient = HttpClient.newBuilder().build();

        // Request the original object from S3.
        var presignedResponse = httpClient.send(
                HttpRequest.newBuilder(new URI(event.inputS3Url())).GET().build(),
                HttpResponse.BodyHandlers.ofInputStream());

        // Consume the incoming response body from the presigned request,
        // apply our transformation on that data, and emit the transformed bytes
        // into the body of the WriteGetObjectResponse request as soon as they're ready.
        // This example compresses the data from S3, but any processing pertinent
        // to your application can be performed here.
        var bodyStream = new GZIPCompressingInputStream(presignedResponse.body());

        // Stream the bytes back to the caller.
        s3Client.writeGetObjectResponse(WriteGetObjectResponseRequest.builder()
                .requestRoute(event.outputRoute())
                .requestToken(event.outputToken())
                .build(),
                RequestBody.fromInputStream(bodyStream,
                    presignedResponse.headers().firstValueAsLong("content-length").orElse(-1L)));
    }
}
```

------
#### [ Python ]



```
import boto3
import requests
import zlib
from botocore.config import Config


"""
A helper class to work with content iterators. Takes an interator and compresses the bytes that come from it. It
implements 'read' and '__iter__' so that the SDK can stream the response. 
"""
class Compress:
    def __init__(self, content_iter):
        self.content = content_iter
        self.compressed_obj = zlib.compressobj()

    def read(self, _size):
        for data in self.__iter__()
            return data

    def __iter__(self):
        while True:
            data = next(self.content)
            chunk = self.compressed_obj.compress(data)
            if not chunk:
                break

            yield chunk

        yield self.compressed_obj.flush()


def handler(event, context):
    """
    Setting the 'payload_signing_enabled' property to False allows us to send a streamed response back to the client.
    in this scenario, a streamed response means that the bytes are not buffered into memory as we're compressing them,
    but instead are sent straight to the user.
    """
    my_config = Config(
        region_name='eu-west-1',
        signature_version='s3v4',
        s3={
            "payload_signing_enabled": False
        }
    )
    s3 = boto3.client('s3', config=my_config)

    """
    Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
    should be delivered and has a presigned URL in 'inputS3Url' where we can download the requested object from.
    The 'userRequest' object has information related to the user who made this 'GetObject' request to S3 Object Lambda.
    """
    get_context = event["getObjectContext"]
    route = get_context["outputRoute"]
    token = get_context["outputToken"]
    s3_url = get_context["inputS3Url"]

    # Compress the 'get' request stream.
    with requests.get(s3_url, stream=True) as r:
        compressed = Compress(r.iter_content())

        # Send the stream back to the client.
        s3.write_get_object_response(Body=compressed, RequestRoute=route, RequestToken=token, ContentType="text/plain",
                                     ContentEncoding="gzip")

    # Gracefully exit the Lambda function.
    return {'status_code': 200}
```

------
#### [ Node.js ]



```
const { S3 } = require('aws-sdk');
const axios = require('axios').default;
const zlib = require('zlib');

exports.handler = async (event) => {
    const s3 = new S3();

    // Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
    // should be delivered and has a presigned URL in 'inputS3Url' where we can download the requested object from.
    const { getObjectContext } = event;
    const { outputRoute, outputToken, inputS3Url } = getObjectContext;

    // Download the object from S3 and process it as a stream, because it might be a huge object and we don't want to
    // buffer it in memory. Note the use of 'await' because we want to wait for 'writeGetObjectResponse' to finish 
    // before we can exit the Lambda function. 
    await axios({
        method: 'GET',
        url: inputS3Url,
        responseType: 'stream',
    }).then(
        // Gzip the stream.
        response => response.data.pipe(zlib.createGzip())
    ).then(
        // Finally send the gzip-ed stream back to the client.
        stream => s3.writeGetObjectResponse({
            RequestRoute: outputRoute,
            RequestToken: outputToken,
            Body: stream,
            ContentType: "text/plain",
            ContentEncoding: "gzip",
        }).promise()
    );

    // Gracefully exit the Lambda function.
    return { statusCode: 200 };
}
```

------

**nota**  
Aunque S3 Object Lambda permite hasta 60 segundos para enviar una respuesta completa al intermediario a través de la solicitud `WriteGetObjectResponse`, el tiempo disponible podría ser menor. Por ejemplo, el tiempo de espera de la función de Lambda puede ser inferior a 60 segundos. En otros casos, el intermediario puede tener tiempos de espera más estrictos. 

Para que el intermediario original reciba una respuesta que no sea el código de estado HTTP 500 (Error interno del servidor), la llamada de `WriteGetObjectResponse` debe completarse. Si la función de Lambda realiza la devolución, mediante con una excepción o de otro modo, antes de que se llame a la API `WriteGetObjectResponse`, el intermediario original recibirá una respuesta 500 (Error interno del servidor). Las excepciones lanzadas durante el tiempo que se tarda en completar la respuesta dan como resultado respuestas truncadas al intermediario. Si la función de Lambda recibe un código de estado de HTTP 200 (OK) de la llamada a la API `WriteGetObjectResponse`, entonces el intermediario original ha enviado la solicitud completa. La respuesta de la función de Lambda, independientemente de si se produce una excepción o no, es ignorada por S3 Object Lambda.

Cuando se llama a la operación de API `WriteGetObjectResponse`, Amazon S3 requiere la ruta y el token de solicitud del contexto del evento. Para obtener más información, consulte [Formato y uso del contexto del evento](olap-event-context.md).

Los parámetros de la ruta y del token de solicitud son necesarios para conectar la respuesta de `WriteGetObjectResult` con el intermediario original. Aunque siempre es conveniente reintentar las respuestas 500 (Error interno del servidor), debido a que el token de solicitud es un token de un solo uso, los intentos posteriores de utilizarlo podrían dar lugar a respuestas de código de estado HTTP 400 (Solicitud incorrecta). Aunque la llamada a `WriteGetObjectResponse` con la ruta los tokens de solicitud no necesariamente debe realizarse desde la función de Lambda invocada, sí debe realizarla una identidad de la misma cuenta. La llamada también debe completarse antes de que la función de Lambda finalice la ejecución.

## Trabajar con solicitudes `HeadObject` en Lambda
<a name="olap-headobject"></a>

En esta sección se asume que el punto de acceso de Object Lambda está configurado para llamar a la función de Lambda para `HeadObject`. Lambda recibirá una carga JSON que contiene una clave llamada `headObjectContext`. Dentro del contexto, hay una sola propiedad llamada `inputS3Url`, que es una URL prefirmada para el punto de acceso de soporte para `HeadObject`.

La URL prefirmada incluirá las siguientes propiedades si se especifican: 
+ `versionId` (en los parámetros de la consulta)
+ `requestPayer` (en el encabezado `x-amz-request-payer`)
+ `expectedBucketOwner` (en el encabezado `x-amz-expected-bucket-owner`)

Otras propiedades no estarán prefirmadas y, por lo tanto, no se incluirán. Las opciones no firmadas que se envían como encabezados se pueden añadir manualmente a la solicitud al llamar a la URL prefirmada que se encuentra en los encabezados `userRequest`. No se admiten las opciones de cifrado del lado del servidor para `HeadObject`.

Para conocer los parámetros URI de la sintaxis de la solicitud, consulte [https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html) en la *Referencia de la API de Amazon Simple Storage Service*

En el siguiente ejemplo, se muestra una carga de entrada JSON de Lambda para `HeadObject`.

```
{
  "xAmzRequestId": "requestId",
  "**headObjectContext**": {
    "**inputS3Url**": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/example?X-Amz-Security-Token=<snip>"
  },
  "configuration": {
       "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap",
       "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap",
       "payload": "{}"
  },
  "userRequest": {
       "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example",
       "headers": {
           "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com",
           "Accept-Encoding": "identity",
           "X-Amz-Content-SHA256": "e3b0c44298fc1example"
       }
   },
   "userIdentity": {
       "type": "AssumedRole",
       "principalId": "principalId",
       "arn": "arn:aws:sts::111122223333:assumed-role/Admin/example",       
       "accountId": "111122223333",
       "accessKeyId": "accessKeyId",
       "sessionContext": {
            "attributes": {
            "mfaAuthenticated": "false",
            "creationDate": "Wed Mar 10 23:41:52 UTC 2021"
       },
       "sessionIssuer": {
            "type": "Role",
            "principalId": "principalId",
            "arn": "arn:aws:iam::111122223333:role/Admin",
            "accountId": "111122223333",
            "userName": "Admin"
            }
       }
    },
  "protocolVersion": "1.00"
}
```

La función de Lambda debe devolver un objeto JSON que contenga los encabezados y los valores que se devolverán para la llamada `HeadObject`.

En el siguiente ejemplo se muestra la estructura del JSON de la respuesta de Lambda para `HeadObject`. 

```
{
    "statusCode": <number>; // Required
    "errorCode": <string>;
    "errorMessage": <string>;
    "headers": {
        "Accept-Ranges": <string>,
        "x-amz-archive-status": <string>,
        "x-amz-server-side-encryption-bucket-key-enabled": <boolean>,
        "Cache-Control": <string>,
        "Content-Disposition": <string>,
        "Content-Encoding": <string>,
        "Content-Language": <string>,
        "Content-Length": <number>, // Required
        "Content-Type": <string>,
        "x-amz-delete-marker": <boolean>,
        "ETag": <string>,
        "Expires": <string>,
        "x-amz-expiration": <string>,
        "Last-Modified": <string>,
        "x-amz-missing-meta": <number>,
        "x-amz-object-lock-mode": <string>,
        "x-amz-object-lock-legal-hold": <string>,
        "x-amz-object-lock-retain-until-date": <string>,
        "x-amz-mp-parts-count": <number>,
        "x-amz-replication-status": <string>,
        "x-amz-request-charged": <string>,
        "x-amz-restore": <string>,
        "x-amz-server-side-encryption": <string>,
        "x-amz-server-side-encryption-customer-algorithm": <string>,
        "x-amz-server-side-encryption-aws-kms-key-id": <string>,
        "x-amz-server-side-encryption-customer-key-MD5": <string>,
        "x-amz-storage-class": <string>,
        "x-amz-tagging-count": <number>,
        "x-amz-version-id": <string>,
        <x-amz-meta-headers>: <string>, // user-defined metadata 
        "x-amz-meta-meta1": <string>, // example of the user-defined metadata header, it will need the x-amz-meta prefix
        "x-amz-meta-meta2": <string>
        ...
    };
}
```

El siguiente ejemplo muestra cómo utilizar la URL prefirmada para rellenar la respuesta modificando los valores del encabezado según sea necesario antes de devolver el JSON.

------
#### [ Python ]



```
import requests

def lambda_handler(event, context):
    print(event)
    
    # Extract the presigned URL from the input.
    s3_url = event["headObjectContext"]["inputS3Url"]

    # Get the head of the object from S3.     
    response = requests.head(s3_url)
    
    # Return the error to S3 Object Lambda (if applicable).           
    if (response.status_code >= 400):
        return {
            "statusCode": response.status_code,
            "errorCode": "RequestFailure",                         
            "errorMessage": "Request to S3 failed"    
    }
    
    # Store the headers in a dictionary.
    response_headers = dict(response.headers)

    # This obscures Content-Type in a transformation, it is optional to add
    response_headers["Content-Type"] = "" 

    # Return the headers to S3 Object Lambda.     
    return {
        "statusCode": response.status_code,
        "headers": response_headers     
        }
```

------

## Trabajar con solicitudes `ListObjects` en Lambda
<a name="olap-listobjects"></a>

En esta sección se asume que el punto de acceso de Object Lambda está configurado para llamar a la función de Lambda para `ListObjects`. Lambda recibirá la carga JSON con un nuevo objeto llamado `listObjectsContext`. `listObjectsContext` contiene una sola propiedad, `inputS3Url`, que es una URL prefirmada para el punto de acceso de soporte para `ListObjects`.

A diferencia de `GetObject` y `HeadObject`, la URL prefirmada incluirá las siguientes propiedades si se especifican:
+ Todos los parámetros de la consulta
+ `requestPayer` (en el encabezado `x-amz-request-payer`) 
+ `expectedBucketOwner` (en el encabezado `x-amz-expected-bucket-owner`)

Para conocer los parámetros URI de la sintaxis de la solicitud, consulte [https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html) en la *Referencia de la API de Amazon Simple Storage Service*

**importante**  
Recomendamos que utilice la versión más reciente, [ListObjectSv2](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html), cuando desarrolle aplicaciones. Para la compatibilidad con versiones anteriores, Amazon S3 sigue siendo compatible con `ListObjects`.

En el siguiente ejemplo, se muestra la carga de entrada JSON de Lambda para `ListObjects`.

```
{
    "xAmzRequestId": "requestId",
     "**listObjectsContext**": {
     "**inputS3Url**": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/?X-Amz-Security-Token=<snip>",
     },
    "configuration": {
        "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap",
        "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap",
        "payload": "{}"
    },
    "userRequest": {
        "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example",
        "headers": {
            "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com",
            "Accept-Encoding": "identity",
            "X-Amz-Content-SHA256": "e3b0c44298fc1example"
        }
    },
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "principalId",
        "arn": "arn:aws:sts::111122223333:assumed-role/Admin/example",
        "accountId": "111122223333",
        "accessKeyId": "accessKeyId",
        "sessionContext": {
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "Wed Mar 10 23:41:52 UTC 2021"
            },
            "sessionIssuer": {
                "type": "Role",
                "principalId": "principalId",
                "arn": "arn:aws:iam::111122223333:role/Admin",
                "accountId": "111122223333",
                "userName": "Admin"
            }
        }
    },
  "protocolVersion": "1.00"
}
```

La función de Lambda debe devolver un objeto JSON que contenga el código de estado, el resultado XML de la lista o la información de error que devolverá el objeto S3 Lambda.

S3 Object Lambda no procesa ni valida `listResultXml`, pero en su lugar lo reenvía al intermediario de `ListObjects`. Para `listBucketResult`, S3 Object Lambda espera que determinadas propiedades sean de un tipo específico y generará excepciones si no puede analizarlas. No se pueden proporcionar `listResultXml` y `listBucketResult` al mismo tiempo.

En el siguiente ejemplo se muestra cómo usar la URL prefirmada para llamar a Amazon S3 y usar el resultado para rellenar una respuesta, incluida la comprobación de errores.

------
#### [ Python ]

```
import requests 
import xmltodict

def lambda_handler(event, context):
    # Extract the presigned URL from the input.
    s3_url = event["listObjectsContext"]["inputS3Url"]


    # Get the head of the object from Amazon S3.
    response = requests.get(s3_url)

    # Return the error to S3 Object Lambda (if applicable).
    if (response.status_code >= 400):
        error = xmltodict.parse(response.content)
        return {
            "statusCode": response.status_code,
            "errorCode": error["Error"]["Code"],
            "errorMessage": error["Error"]["Message"]
        }

    # Store the XML result in a dict.
    response_dict = xmltodict.parse(response.content)

    # This obscures StorageClass in a transformation, it is optional to add
    for item in response_dict['ListBucketResult']['Contents']:
        item['StorageClass'] = ""

    # Convert back to XML.
    listResultXml = xmltodict.unparse(response_dict)
    
    # Create response with listResultXml.
    response_with_list_result_xml = {
        'statusCode': 200,
        'listResultXml': listResultXml
    }

    # Create response with listBucketResult.
    response_dict['ListBucketResult'] = sanitize_response_dict(response_dict['ListBucketResult'])
    response_with_list_bucket_result = {
        'statusCode': 200,
        'listBucketResult': response_dict['ListBucketResult']
    }

    # Return the list to S3 Object Lambda.
    # Can return response_with_list_result_xml or response_with_list_bucket_result
    return response_with_list_result_xml

# Converting the response_dict's key to correct casing
def sanitize_response_dict(response_dict: dict):
    new_response_dict = dict()
    for key, value in response_dict.items():
        new_key = key[0].lower() + key[1:] if key != "ID" else 'id'
        if type(value) == list:
            newlist = []
            for element in value:
                if type(element) == type(dict()):
                    element = sanitize_response_dict(element)
                newlist.append(element)
            value = newlist
        elif type(value) == dict:
            value = sanitize_response_dict(value)
        new_response_dict[new_key] = value
    return new_response_dict
```

------

En el siguiente ejemplo se muestra la estructura del JSON de la respuesta de Lambda para `ListObjects`. 

```
{ 
  "statusCode": <number>; // Required
  "errorCode": <string>;
  "errorMessage": <string>;
  "listResultXml": <string>; // This can also be Error XML string in case S3 returned error response when calling the pre-signed URL

  "listBucketResult": {  // listBucketResult can be provided instead of listResultXml, however they can not both be provided in the JSON response  
        "name": <string>,  // Required for 'listBucketResult'
        "prefix": <string>,  
        "marker": <string>, 
        "nextMarker": <string>, 
        "maxKeys": <int>,   // Required for 'listBucketResult'
        "delimiter": <string>, 
        "encodingType": <string>  
        "isTruncated": <boolean>,  // Required for 'listBucketResult'
        "contents": [  { 
            "key": <string>,  // Required for 'content'
            "lastModified": <string>,  
            "eTag": <string>,  
            "checksumAlgorithm": <string>,   // CRC32,  CRC32C,  SHA1,  SHA256
            "size": <int>,   // Required for 'content'
            "owner": {  
                "displayName": <string>,  // Required for 'owner'
                "id": <string>,  // Required for 'owner'
            },  
            "storageClass": <string>  
            },  
        ...  
        ],  
        "commonPrefixes": [  {  
            "prefix": <string>   // Required for 'commonPrefix'
        },  
        ...  
        ],  
    }
}
```

## Trabajar con solicitudes `ListObjectsV2` en Lambda
<a name="olap-listobjectsv2"></a>

En esta sección se asume que el punto de acceso de Object Lambda está configurado para llamar a la función de Lambda para `ListObjectsV2`. Lambda recibirá la carga JSON con un nuevo objeto llamado `listObjectsV2Context`. `listObjectsV2Context` contiene una sola propiedad, `inputS3Url`, que es una URL prefirmada para el punto de acceso de soporte para `ListObjectsV2`.

A diferencia de `GetObject` y `HeadObject`, la URL prefirmada incluirá las siguientes propiedades si se especifican: 
+ Todos los parámetros de la consulta
+ `requestPayer` (en el encabezado `x-amz-request-payer`) 
+ `expectedBucketOwner` (en el encabezado `x-amz-expected-bucket-owner`)

Para conocer los parámetros URI de la sintaxis de la solicitud, consulte [https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html) en la *Referencia de la API de Amazon Simple Storage Service*

En el siguiente ejemplo, se muestra la carga de entrada JSON de Lambda para `ListObjectsV2`.

```
{
    "xAmzRequestId": "requestId",
     "**listObjectsV2Context**": {
     "**inputS3Url**": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/?list-type=2&X-Amz-Security-Token=<snip>",
     },
    "configuration": {
        "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap",
        "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap",
        "payload": "{}"
    },
    "userRequest": {
        "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example",
        "headers": {
            "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com",
            "Accept-Encoding": "identity",
            "X-Amz-Content-SHA256": "e3b0c44298fc1example"
        }
    },
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "principalId",
        "arn": "arn:aws:sts::111122223333:assumed-role/Admin/example",
        "accountId": "111122223333",
        "accessKeyId": "accessKeyId",
        "sessionContext": {
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "Wed Mar 10 23:41:52 UTC 2021"
            },
            "sessionIssuer": {
                "type": "Role",
                "principalId": "principalId",
                "arn": "arn:aws:iam::111122223333:role/Admin",
                "accountId": "111122223333",
                "userName": "Admin"
            }
        }
    },
  "protocolVersion": "1.00" 
}
```

La función de Lambda debe devolver un objeto JSON que contenga el código de estado, el resultado XML de la lista o la información de error que devolverá el objeto S3 Lambda.

S3 Object Lambda no procesa ni valida `listResultXml`, pero en su lugar lo reenvía al intermediario de `ListObjectsV2`. Para `listBucketResult`, S3 Object Lambda espera que determinadas propiedades sean de un tipo específico y generará excepciones si no puede analizarlas. No se pueden proporcionar `listResultXml` y `listBucketResult` al mismo tiempo.

En el siguiente ejemplo se muestra cómo usar la URL prefirmada para llamar a Amazon S3 y usar el resultado para rellenar una respuesta, incluida la comprobación de errores.

------
#### [ Python ]

```
import requests 
import xmltodict

def lambda_handler(event, context):
    # Extract the presigned URL from the input.
    s3_url = event["listObjectsV2Context"]["inputS3Url"]


    # Get the head of the object from Amazon S3.
    response = requests.get(s3_url)

    # Return the error to S3 Object Lambda (if applicable).
    if (response.status_code >= 400):
        error = xmltodict.parse(response.content)
        return {
            "statusCode": response.status_code,
            "errorCode": error["Error"]["Code"],
            "errorMessage": error["Error"]["Message"]
        }

    # Store the XML result in a dict.
    response_dict = xmltodict.parse(response.content)

    # This obscures StorageClass in a transformation, it is optional to add
    for item in response_dict['ListBucketResult']['Contents']:
        item['StorageClass'] = ""

    # Convert back to XML.
    listResultXml = xmltodict.unparse(response_dict)
    
    # Create response with listResultXml.
    response_with_list_result_xml = {
        'statusCode': 200,
        'listResultXml': listResultXml
    }

    # Create response with listBucketResult.
    response_dict['ListBucketResult'] = sanitize_response_dict(response_dict['ListBucketResult'])
    response_with_list_bucket_result = {
        'statusCode': 200,
        'listBucketResult': response_dict['ListBucketResult']
    }

    # Return the list to S3 Object Lambda.
    # Can return response_with_list_result_xml or response_with_list_bucket_result
    return response_with_list_result_xml

# Converting the response_dict's key to correct casing
def sanitize_response_dict(response_dict: dict):
    new_response_dict = dict()
    for key, value in response_dict.items():
        new_key = key[0].lower() + key[1:] if key != "ID" else 'id'
        if type(value) == list:
            newlist = []
            for element in value:
                if type(element) == type(dict()):
                    element = sanitize_response_dict(element)
                newlist.append(element)
            value = newlist
        elif type(value) == dict:
            value = sanitize_response_dict(value)
        new_response_dict[new_key] = value
    return new_response_dict
```

------

En el siguiente ejemplo se muestra la estructura del JSON de la respuesta de Lambda para `ListObjectsV2`. 

```
{  
    "statusCode": <number>; // Required  
    "errorCode": <string>;  
    "errorMessage": <string>;  
    "listResultXml": <string>; // This can also be Error XML string in case S3 returned error response when calling the pre-signed URL  
  
    "listBucketResult": {  // listBucketResult can be provided instead of listResultXml, however they can not both be provided in the JSON response 
        "name": <string>, // Required for 'listBucketResult'  
        "prefix": <string>,  
        "startAfter": <string>,  
        "continuationToken": <string>,  
        "nextContinuationToken": <string>,
        "keyCount": <int>, // Required for 'listBucketResult'  
        "maxKeys": <int>, // Required for 'listBucketResult'  
        "delimiter": <string>,  
        "encodingType": <string>  
        "isTruncated": <boolean>, // Required for 'listBucketResult'  
        "contents": [ {  
            "key": <string>, // Required for 'content'  
            "lastModified": <string>,  
            "eTag": <string>,  
            "checksumAlgorithm": <string>, // CRC32, CRC32C, SHA1, SHA256  
            "size": <int>, // Required for 'content'  
            "owner": {  
                "displayName": <string>, // Required for 'owner'  
                "id": <string>, // Required for 'owner'  
            },  
            "storageClass": <string>  
            },  
            ...  
        ],  
        "commonPrefixes": [ {  
            "prefix": <string> // Required for 'commonPrefix'  
            },  
        ...  
        ],  
    }  
}
```

# Formato y uso del contexto del evento
<a name="olap-event-context"></a>

**nota**  
A partir del 7 de noviembre de 2025, S3 Object Lambda solo estará disponible para los clientes actuales que utilizan el servicio actualmente, así como para socios seleccionados de la red de socios de AWS (APN). Para obtener más información sobre capacidades similares a las de S3 Object Lambda, haga clic aquí: [Cambio de disponibilidad de Amazon S3 Object Lambda](https://docs.aws.amazon.com/AmazonS3/latest/userguide/amazons3-ol-change.html).

Amazon S3 Object Lambda proporciona contexto sobre la solicitud que se realiza en el evento transferido a la función de AWS Lambda. A continuación se muestra un ejemplo de solicitud: Las descripciones de los campos se incluyen después del ejemplo.

```
{
    "xAmzRequestId": "requestId",
    "getObjectContext": {
        "inputS3Url": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/example?X-Amz-Security-Token=<snip>",
        "outputRoute": "io-use1-001",
        "outputToken": "OutputToken"
    },
    "configuration": {
        "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap",
        "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap",
        "payload": "{}"
    },
    "userRequest": {
        "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example",
        "headers": {
            "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com",
            "Accept-Encoding": "identity",
            "X-Amz-Content-SHA256": "e3b0c44298fc1example"
        }
    },
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "principalId",
        "arn": "arn:aws:sts::111122223333:assumed-role/Admin/example",
        "accountId": "111122223333",
        "accessKeyId": "accessKeyId",
        "sessionContext": {
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "Wed Mar 10 23:41:52 UTC 2021"
            },
            "sessionIssuer": {
                "type": "Role",
                "principalId": "principalId",
                "arn": "arn:aws:iam::111122223333:role/Admin",
                "accountId": "111122223333",
                "userName": "Admin"
            }
        }
    },
    "protocolVersion": "1.00"
}
```

Los siguientes campos están incluidos en la solicitud:
+ `xAmzRequestId`: ID de solicitud de Amazon S3 para esta solicitud. Le recomendamos que registre este valor para ayudar con la depuración.
+ `getObjectContext`: detalles de entrada y salida de las conexiones a Amazon S3 y S3 Object Lambda.
  + `inputS3Url`: URL prefirmada que se puede utilizar para obtener el objeto original de Amazon S3. La URL está firmada utilizando la identidad del intermediario que la llamó originalmente, y los permisos de ese usuario se aplicarán cuando se utilice la URL. Si hay encabezados firmados en la URL, la función de Lambda debe incluir estos encabezados los en la llamada a Amazon S3, excepto en el caso del encabezado `Host`.
  + `outputRoute` – token de enrutamiento que se agrega a la URL de S3 Object Lambda cuando la función de Lambda llama a `WriteGetObjectResponse`.
  + `outputToken`: token opaco utilizado por S3 Object Lambda para hacer coincidir la llamada `WriteGetObjectResponse` con el intermediario original.
+ `configuration`: información de configuración sobre el punto de acceso de Object Lambda.
  + `accessPointArn`: nombre de recurso de Amazon (ARN) del punto de acceso de Object Lambda que ha recibido esta solicitud.
  + `supportingAccessPointArn`: ARN del punto de acceso de apoyo especificado en la configuración del punto de acceso de Object Lambda.
  + `payload`: datos personalizados que se aplican a la configuración del punto de acceso de Object Lambda. S3 Object Lambda trata estos datos como una cadena opaca, por lo que es posible que deba decodificarse antes de su uso.
+ `userRequest`: información sobre la llamada original a S3 Object Lambda.
  + `url`: URL decodificada de la solicitud recibida por S3 Object Lambda, excluyendo cualquier parámetro de consulta relacionado con la autorización.
  + `headers`: un mapa de cadena a cadenas que contienen los encabezados HTTP y los valores de la llamada original, excluyendo cualquier encabezado relacionado con la autorización. Si el mismo encabezado aparece varias veces, los valores de cada instancia del mismo encabezado se combinan en una lista delimitada por comas. Las mayúsculas y minúsculas de los encabezados originales se conservan en este mapa.
+ `userIdentity`: detalles sobre la identidad que hizo la llamada a S3 Object Lambda. Para obtener más información, consulte [Registro de eventos de datos para registros de seguimiento](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-data-events-with-cloudtrail.html) en la *Guía del usuario de AWS CloudTrail*.
  + `type`: tipo de identidad.
  + `accountId` – la Cuenta de AWS a la que pertenece la identidad.
  + `userName`: nombre descriptivo de la identidad que realizó la llamada.
  + `principalId`: identificador único para la identidad que ha efectuado la llamada.
  + `arn`: ARN de la entidad principal que hizo la llamada. La última sección del ARN contiene el usuario o el rol que realizó la llamada.
  + `sessionContext`: si la solicitud se realizó con credenciales de seguridad temporales, este elemento proporciona información sobre la sesión que se creó para esas credenciales.
  + `invokedBy`: nombre del Servicio de AWS que realizó la solicitud, como Amazon EC2 Auto Scaling o AWS Elastic Beanstalk.
  + `sessionIssuer`: si la solicitud se realizó con credenciales de seguridad temporales, este elemento proporciona información acerca de cómo se obtuvieron las credenciales.
+ `protocolVersion`: ID de versión del contexto proporcionado. El formato de este campo es `{Major Version}.{Minor Version}`. Los números de versión secundaria son siempre números de dos dígitos. Cualquier eliminación o cambio en la semántica de un campo precisa un bache de versión principal y requiere la opción opcional activa. Amazon S3 puede agregar nuevos campos en cualquier momento, momento en el que podría experimentar un problema de versión secundaria. Por la naturaleza de las implementaciones de software, puede que vea varias versiones secundarias en uso a la vez.

# Trabajar con encabezados Range y partNumber
<a name="range-get-olap"></a>

**nota**  
A partir del 7 de noviembre de 2025, S3 Object Lambda solo estará disponible para los clientes actuales que utilizan el servicio actualmente, así como para socios seleccionados de la red de socios de AWS (APN). Para obtener más información sobre capacidades similares a las de S3 Object Lambda, haga clic aquí: [Cambio de disponibilidad de Amazon S3 Object Lambda](https://docs.aws.amazon.com/AmazonS3/latest/userguide/amazons3-ol-change.html).

Cuando se trabaja con objetos grandes en Amazon S3 Object Lambda, puede utilizar el encabezado HTTP `Range` para descargar un rango de bytes especificado de un objeto. Para obtener diferentes rangos de bytes dentro del mismo objeto, puede utilizar conexiones concurrentes a Amazon S3. También puede usar el parámetro `partNumber` (número entero entre 1 y 10 000) que realiza una solicitud de rango para la parte especificada del objeto.

Puesto que hay varias formas de gestionar una solicitud que incluya los parámetros `Range` o `partNumber`, S3 Object Lambda no aplica estos parámetros al objeto transformado. En su lugar, la función de AWS Lambda debe implementar esta funcionalidad según sea necesario para la aplicación.

Para usar los parámetros `Range` y `partNumber` con S3 Object Lambda, haga lo siguiente: 
+ Active estos parámetros en la configuración del punto de acceso de Object Lambda.
+ Escriba una función de Lambda que pueda gestionar las solicitudes que incluyan estos parámetros.

Esta operación se describe en los siguientes pasos.

## Paso 1: Configurar el punto de acceso de Object Lambda
<a name="range-get-olap-step-1"></a>

De forma predeterminada, los puntos de acceso de Object Lambda responden con un código de estado HTTP 501 (No implementado) a cualquier solicitud `GetObject` o `HeadObject` que contenga un parámetro `Range` o `partNumber`, ya sea en los encabezados o en los parámetros de consulta. 

Para permitir que un punto de acceso de Object Lambda acepte este tipo de solicitudes, debe incluir `GetObject-Range`, `GetObject-PartNumber`, `HeadObject-Range` o `HeadObject-PartNumber` en la sección `AllowedFeatures` de la configuración de su punto de acceso de Object Lambda. Para obtener más información acerca de la actualización de la configuración del punto de acceso de Object Lambda, consulte [Creación de puntos de acceso Object Lambda](olap-create.md). 

## Paso 2: Implementar el tratamiento de `Range` o `partNumber` en la función de Lambda
<a name="range-get-olap-step-2"></a>

Cuando el punto de acceso de Object Lambda invoque la función de Lambda con una solicitud `GetObject` o `HeadObject` de rango, los parámetros `Range` o `partNumber` se incluyen en el contexto del evento. La ubicación del parámetro en el contexto del evento depende del parámetro que se haya utilizado y de cómo se incluyó en la solicitud original al punto de acceso de Object Lambda, tal como se explica en la tabla siguiente. 


| Parámetro | Ubicación en el contexto del evento | 
| --- | --- | 
|  `Range` (encabezado)  |  `userRequest.headers.Range`  | 
|  `Range` (parámetro de consulta)  |  `userRequest.url` (parámetro de consulta `Range`)  | 
|  `partNumber`  |  `userRequest.url` (parámetro de consulta `partNumber`)  | 

**importante**  
La URL prefirmada proporcionada para su punto de acceso de Object Lambda no contiene el parámetro `Range` o `partNumber` de la solicitud original. Consulte las siguientes opciones sobre cómo manejar estos parámetros en la función de AWS Lambda.

Después de extraer el valor de `Range` o `partNumber`, puede adoptar uno de los siguientes enfoques en función de las necesidades de su aplicación:

1. **Asigne el `Range` o `partNumber` solicitados al objeto transformado (recomendado).** 

   La forma más fiable de gestionar solicitudes `Range` o `partNumber` es hacer lo siguiente: 
   + Recupere el objeto completo de Amazon S3.
   + Transforme el objeto.
   + Aplique los parámetros `Range` o `partNumber` solicitados al objeto transformado.

   Para ello, utilice la URL prefirmada proporcionada para obtener el objeto completo de Amazon S3 y, a continuación, procesarlo según sea necesario. Para ver un ejemplo de función de Lambda que procesa así un parámetro `Range`, consulte [este ejemplo](https://github.com/aws-samples/amazon-s3-object-lambda-default-configuration/blob/main/function/nodejs_20_x/src/response/range_mapper.ts) en el repositorio AWS de GitHub.

1. **Asigne el `Range` solicitado a la URL prefirmada.**

   En algunos casos la función de Lambda puede asignar el `Range` solicitado directamente a la URL prefirmada para recuperar solo parte del objeto de Amazon S3. Este enfoque es adecuado solo si la transformación cumple los dos criterios siguientes:

   1. La función de transformación se puede aplicar a rangos de objetos parciales.

   1. La aplicación del parámetro `Range` antes o después de la función de transformación da como resultado el mismo objeto transformado.

   Por ejemplo, una función de transformación que convierte en mayúsculas todos los caracteres de un objeto codificado en ASCII cumple los dos criterios anteriores. La transformación se puede aplicar a parte de un objeto y aplicar el parámetro `Range` antes de la transformación da el mismo resultado que aplicar el parámetro después de la transformación.

   Por el contrario, una función que invierte los caracteres de un objeto codificado en ASCII no cumple estos criterios. Dicha función cumple el criterio 1, ya que se puede aplicar a rangos de objetos parciales. No obstante, no cumple el criterio 2, porque cuando se aplica el parámetro `Range` de la transformación se obtienen resultados distintos que si se aplica el parámetro después de la transformación. 

   Considere una solicitud para aplicar la función a los tres primeros caracteres de un objeto con el contenido `abcdefg`. Aplicar el parámetro `Range` antes de la transformación solo recupera `abc` y luego invierte los datos, devolviendo `cba`. Pero si el parámetro se aplica después de la transformación, la función recupera todo el objeto, lo invierte y, a continuación, aplica el parámetro `Range`, devolviendo `gfe`. Puesto que los resultados son diferentes, esta función no debe aplicar el parámetro `Range` cuando se recupera el objeto de Amazon S3. En su lugar, debe recuperar el objeto completo, realizar la transformación y solo después aplicar el parámetro `Range`. 
**aviso**  
En muchos casos si se aplica el parámetro `Range` a la URL prefirmada, el resultado será un comportamiento inesperado por parte de la función de Lambda o el cliente solicitante. A menos que tenga la seguridad de que su aplicación funcionará correctamente cuando se recupere solo un objeto parcial de Amazon S3, recomendamos que recupere y transforme objetos completos como se describió anteriormente en el enfoque A. 

   Si la solicitud cumple los criterios descritos anteriormente en este método B, puede simplificar la función AWS Lambda recuperando solo el intervalo de objetos solicitado y, a continuación, ejecutando la transformación en ese intervalo. 

   En el siguiente ejemplo de código Java, se muestra cómo realizar lo siguiente: 
   + Recupere el encabezado `Range` de la solicitud `GetObject`.
   + Añada el encabezado `Range` a la URL prefirmada que Lambda puede utilizar para recuperar el rango solicitado desde Amazon S3.

   ```
   private HttpRequest.Builder applyRangeHeader(ObjectLambdaEvent event, HttpRequest.Builder presignedRequest) {
       var header = event.getUserRequest().getHeaders().entrySet().stream()
               .filter(e -> e.getKey().toLowerCase(Locale.ROOT).equals("range"))
               .findFirst();
   
       // Add check in the query string itself.
       header.ifPresent(entry -> presignedRequest.header(entry.getKey(), entry.getValue()));
       return presignedRequest;
   }
   ```