Working with Range and partNumber headers
When working with large objects in Amazon S3 Object Lambda, you can use the Range
HTTP
header to download a specified byte range from an object. To fetch different byte ranges
from within the same object, you can use concurrent connections to Amazon S3. You can also
specify the partNumber
parameter (an integer between 1 and 10,000), which
performs a ranged request for the specified part of the object.
Because there are multiple ways that you might want to handle a request that includes the
Range
or partNumber
parameters, S3 Object Lambda doesn't apply these
parameters to the transformed object. Instead, your AWS Lambda function must implement this
functionality as needed for your application.
To use the Range
and partNumber
parameters with S3 Object Lambda, you do the
following:
-
Enable these parameters in your Object Lambda Access Point configuration.
-
Write a Lambda function that can handle requests that include these parameters.
The following steps describe how to accomplish this.
Step 1: Configure your Object Lambda Access Point
By default, Object Lambda Access Points respond with an HTTP status code 501 (Not Implemented) error to
any GetObject
or HeadObject
request that contains a
Range
or partNumber
parameter, either in the headers or
query parameters.
To enable an Object Lambda Access Point to accept such requests, you must include GetObject-Range
, GetObject-PartNumber
, HeadObject-Range
, or HeadObject-PartNumber
in the AllowedFeatures
section of your Object Lambda Access Point configuration. For more information about
updating your Object Lambda Access Point configuration, see Creating Object Lambda Access Points.
Step 2: Implement Range
or
partNumber
handling in your Lambda function
When your Object Lambda Access Point invokes your Lambda function with a ranged GetObject
or
HeadObject
request, the Range
or partNumber
parameter is included in the event context. The location of the parameter in the event
context depends on which parameter was used and how it was included in the original
request to the Object Lambda Access Point, as explained in the following table.
Parameter | Event context location |
---|---|
|
|
|
|
|
|
Important
The provided presigned URL for your Object Lambda Access Point doesn't contain the Range
or
partNumber
parameter from the original request. See the following options on how to handle these parameters in your AWS Lambda function.
After you extract the Range
or partNumber
value, you can take
one of the following approaches, based on your application's needs:
-
Map the requested
Range
orpartNumber
to the transformed object (recommended).The most reliable way to handle
Range
orpartNumber
requests is to do the following:-
Retrieve the full object from Amazon S3.
-
Transform the object.
-
Apply the requested
Range
orpartNumber
parameters to the transformed object.
To do this, use the provided presigned URL to fetch the entire object from Amazon S3 and then process the object as needed. For an example Lambda function that processes a
Range
parameter in this way, see this samplein the AWS Samples GitHub repository. -
-
Map the requested
Range
to the presigned URL.In some cases, your Lambda function can map the requested
Range
directly to the presigned URL to retrieve only part of the object from Amazon S3. This approach is appropriate only if your transformation meets both of the following criteria:-
Your transformation function can be applied to partial object ranges.
-
Applying the
Range
parameter before or after the transformation function results in the same transformed object.
For example, a transformation function that converts all characters in an ASCII-encoded object to uppercase meets both of the preceding criteria. The transformation can be applied to part of an object, and applying the
Range
parameter before the transformation achieves the same result as applying it after the transformation.By contrast, a function that reverses the characters in an ASCII-encoded object doesn't meet these criteria. Such a function meets criterion 1, because it can be applied to partial object ranges. However, it doesn't meet criterion 2, because applying the
Range
parameter before the transformation achieves different results than applying the parameter after the transformation.Consider a request to apply the function to the first three characters of an object with the contents
abcdefg
. Applying theRange
parameter before the transformation retrieves onlyabc
and then reverses the data, returningcba
. But if the parameter is applied after the transformation, the function retrieves the entire object, reverses it, and then applies theRange
parameter, returninggfe
. Because these results are different, this function should not apply theRange
parameter when retrieving the object from Amazon S3. Instead, it should retrieve the entire object, perform the transformation, and only then apply theRange
parameter.Warning
In many cases, applying the
Range
parameter to the presigned URL will result in unexpected behavior by the Lambda function or the requesting client. Unless you are sure that your application will work properly when retrieving only a partial object from Amazon S3, we recommend that you retrieve and transform full objects as described earlier in approach A.If your application meets the criteria described earlier in approach B, you can simplify your AWS Lambda function by fetching only the requested object range and then running your transformation on that range.
The following Java code example demonstrates how to do the following:
-
Retrieve the
Range
header from theGetObject
request. -
Add the
Range
header to the presigned URL that Lambda can use to retrieve the requested range from 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; }
-