}
*/
const retry = (config, fn) =>
new Promise((resolve, reject) => {
- const { intervalInMs = 500, maxRetries = 10 } = config;
+ const { intervalInMs = 500, maxRetries = 10, backoff = 0 } = config;
fn()
.then(resolve)
.catch((err) => {
@@ -32,10 +32,14 @@ const retry = (config, fn) =>
config.swallowError ? resolve() : reject(err);
} else {
setTimeout(() => {
- retry({ ...config, maxRetries: maxRetries - 1 }, fn).then(
- resolve,
- reject,
- );
+ retry(
+ {
+ ...config,
+ intervalInMs: intervalInMs + backoff,
+ maxRetries: maxRetries - 1,
+ },
+ fn,
+ ).then(resolve, reject);
}, intervalInMs);
}
});
diff --git a/javav2/example_code/iotsitewise/README.md b/javav2/example_code/iotsitewise/README.md
index 71c24b6f9b5..e970fefafb7 100644
--- a/javav2/example_code/iotsitewise/README.md
+++ b/javav2/example_code/iotsitewise/README.md
@@ -79,7 +79,7 @@ This example shows you how to get started using AWS IoT SiteWise.
#### Learn the basics
-This example shows you how to Learn core operations for AWS IoT SiteWise using an AWS SDK.
+This example shows you how to learn core operations for AWS IoT SiteWise using an AWS SDK.
@@ -116,4 +116,4 @@ in the `javav2` folder.
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
-SPDX-License-Identifier: Apache-2.0
\ No newline at end of file
+SPDX-License-Identifier: Apache-2.0
diff --git a/javav2/example_code/s3/README.md b/javav2/example_code/s3/README.md
index ac0770d46ad..2b4ef31b31b 100644
--- a/javav2/example_code/s3/README.md
+++ b/javav2/example_code/s3/README.md
@@ -98,7 +98,8 @@ functions within the same service.
- [Create a presigned URL for download](src/main/java/com/example/s3/GeneratePresignedGetUrlAndRetrieve.java)
-- [Create a presigned URL for upload](src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithMetadata.java)
+- [Create a presigned URL with metadata in headers for upload](src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithMetadata.java)
+- [Create a presigned URL with query parameters for upload](src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithQueryParams.java)
## Run the examples
diff --git a/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithQueryParams.java b/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithQueryParams.java
new file mode 100644
index 00000000000..c56243006a0
--- /dev/null
+++ b/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithQueryParams.java
@@ -0,0 +1,158 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package com.example.s3;
+
+// snippet-start:[presigned.java2.generatepresignedurlandputfilewithqueryparams.import]
+import com.example.s3.util.PresignUrlUtils;
+import org.slf4j.Logger;
+import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration;
+import software.amazon.awssdk.core.internal.sync.FileContentStreamProvider;
+import software.amazon.awssdk.http.HttpExecuteRequest;
+import software.amazon.awssdk.http.HttpExecuteResponse;
+import software.amazon.awssdk.http.SdkHttpClient;
+import software.amazon.awssdk.http.SdkHttpMethod;
+import software.amazon.awssdk.http.SdkHttpRequest;
+import software.amazon.awssdk.http.apache.ApacheHttpClient;
+import software.amazon.awssdk.services.s3.S3Client;
+import software.amazon.awssdk.services.s3.model.PutObjectRequest;
+import software.amazon.awssdk.services.s3.presigner.S3Presigner;
+import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
+import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.Map;
+import java.util.UUID;
+// snippet-end:[presigned.java2.generatepresignedurlandputfilewithqueryparams.import]
+
+/**
+ * Before running this Java V2 code example, set up your development environment.
+ *
+ * For more information, see the following documentation topic:
+ *
+ * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
+ */
+
+public class GeneratePresignedUrlAndPutFileWithQueryParams {
+ private static final Logger logger = org.slf4j.LoggerFactory.getLogger(GeneratePresignedUrlAndPutFileWithQueryParams.class);
+ private final static S3Client s3Client = S3Client.create();
+
+ public static void main(String[] args) {
+ String bucketName = "amzn-s3-demo-bucket" + UUID.randomUUID(); // Change bucket name.
+ String keyName = "key" + UUID.randomUUID();
+ String resourcePath = "uploadDirectory/file1.txt";
+ // Uncomment the following two lines and comment out the previous two lines to use an image file instead of a PDF file.
+ //String resourcePath = "image.png";
+ //String contentType = "image/png";
+
+ Map queryParams = Map.of(
+ "x-amz-meta-author", "Bob",
+ "x-amz-meta-version", "1.0.0.0",
+ "x-amz-acl", "private",
+ "x-amz-server-side-encryption", "AES256"
+ );
+
+ PresignUrlUtils.createBucket(bucketName, s3Client);
+ GeneratePresignedUrlAndPutFileWithQueryParams presign = new GeneratePresignedUrlAndPutFileWithQueryParams();
+ try {
+ String presignedUrlString = presign.createPresignedUrl(bucketName, keyName, queryParams);
+ presign.useSdkHttpClientToPut(presignedUrlString, getFileForForClasspathResource(resourcePath));
+
+ } finally {
+ PresignUrlUtils.deleteObject(bucketName, keyName, s3Client);
+ PresignUrlUtils.deleteBucket(bucketName, s3Client);
+ }
+ }
+
+ // snippet-start:[presigned.java2.generatepresignedurlandputfilewithqueryparams.main]
+ // snippet-start:[presigned.java2.generatepresignedurlandputfilewithqueryparams.createpresignedurl]
+ /**
+ * Creates a presigned URL to use in a subsequent HTTP PUT request. The code adds query parameters
+ * to the request instead of using headers. By using query parameters, you do not need to add the
+ * the parameters as headers when the PUT request is eventually sent.
+ *
+ * @param bucketName Bucket name where the object will be uploaded.
+ * @param keyName Key name of the object that will be uploaded.
+ * @param queryParams Query string parameters to be added to the presigned URL.
+ * @return
+ */
+ public String createPresignedUrl(String bucketName, String keyName, Map queryParams) {
+ try (S3Presigner presigner = S3Presigner.create()) {
+ // Create an override configuration to store the query parameters.
+ AwsRequestOverrideConfiguration.Builder overrideConfigurationBuilder = AwsRequestOverrideConfiguration.builder();
+
+ queryParams.forEach(overrideConfigurationBuilder::putRawQueryParameter);
+
+ PutObjectRequest objectRequest = PutObjectRequest.builder()
+ .bucket(bucketName)
+ .key(keyName)
+ .overrideConfiguration(overrideConfigurationBuilder.build()) // Add the override configuration.
+ .build();
+
+ PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
+ .signatureDuration(Duration.ofMinutes(10)) // The URL expires in 10 minutes.
+ .putObjectRequest(objectRequest)
+ .build();
+
+
+ PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest);
+ String myURL = presignedRequest.url().toString();
+ logger.info("Presigned URL to upload a file to: [{}]", myURL);
+ logger.info("HTTP method: [{}]", presignedRequest.httpRequest().method());
+
+ return presignedRequest.url().toExternalForm();
+ }
+ }
+ // snippet-end:[presigned.java2.generatepresignedurlandputfilewithqueryparams.createpresignedurl]
+
+ // snippet-start:[presigned.java2.generatepresignedurlandputfilewithqueryparams.sdkhttpclient]
+ /**
+ * Use the AWS SDK for Java V2 SdkHttpClient class to execute the PUT request. Since the
+ * URL contains the query parameters, no headers are needed for metadata, SSE settings, or ACL settings.
+ *
+ * @param presignedUrlString The URL for the PUT request.
+ * @param fileToPut File to uplaod
+ */
+ public void useSdkHttpClientToPut(String presignedUrlString, File fileToPut) {
+ logger.info("Begin [{}] upload", fileToPut.toString());
+
+ try {
+ URL presignedUrl = new URL(presignedUrlString);
+
+ SdkHttpRequest.Builder requestBuilder = SdkHttpRequest.builder()
+ .method(SdkHttpMethod.PUT)
+ .uri(presignedUrl.toURI());
+
+ SdkHttpRequest request = requestBuilder.build();
+
+ HttpExecuteRequest executeRequest = HttpExecuteRequest.builder()
+ .request(request)
+ .contentStreamProvider(new FileContentStreamProvider(fileToPut.toPath()))
+ .build();
+
+ try (SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) {
+ HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call();
+ logger.info("Response code: {}", response.httpResponse().statusCode());
+ }
+ } catch (URISyntaxException | IOException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ // snippet-end:[presigned.java2.generatepresignedurlandputfilewithqueryparams.sdkhttpclient]
+ // snippet-end:[presigned.java2.generatepresignedurlandputfilewithqueryparams.main]
+
+ public static File getFileForForClasspathResource(String resourcePath) {
+ try {
+ URL resource = GeneratePresignedUrlAndUploadObject.class.getClassLoader().getResource(resourcePath);
+ return Paths.get(resource.toURI()).toFile();
+ } catch (URISyntaxException e) {
+ logger.error(e.getMessage(), e);
+ }
+ return null;
+ }
+}
diff --git a/javav2/example_code/s3/src/main/java/com/example/s3/batch/README.md b/javav2/example_code/s3/src/main/java/com/example/s3/batch/README.md
index 0843d40fd05..ea544fd5925 100644
--- a/javav2/example_code/s3/src/main/java/com/example/s3/batch/README.md
+++ b/javav2/example_code/s3/src/main/java/com/example/s3/batch/README.md
@@ -31,7 +31,7 @@ For prerequisites, see the [README](../../../../../../../../../README.md#Prerequ
### Get started
-- [Hello 'Amazon S3 Control'](HelloS3Batch.java#L5) (`ListJobs`)
+- [Hello Amazon S3 Control](HelloS3Batch.java#L5) (`ListJobs`)
### Basics
@@ -65,14 +65,14 @@ Code excerpts that show you how to call individual service functions.
-#### Hello 'Amazon S3 Control'
+#### Hello Amazon S3 Control
-This example shows you how to get started using 'Amazon S3 Control'
+This example shows you how to get started using Amazon S3 Control.
#### Learn the basics
-This example shows you how to learn core operations for'Amazon S3 Control'.
+This example shows you how to learn core operations for Amazon S3 Control.
@@ -109,4 +109,4 @@ in the `javav2` folder.
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
-SPDX-License-Identifier: Apache-2.0
\ No newline at end of file
+SPDX-License-Identifier: Apache-2.0
diff --git a/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedPutUrlTests.java b/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedPutUrlTests.java
index ce331823256..efe6a7690fc 100644
--- a/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedPutUrlTests.java
+++ b/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedPutUrlTests.java
@@ -4,6 +4,7 @@
package com.example.s3.presignurl;
import com.example.s3.GeneratePresignedUrlAndPutFileWithMetadata;
+import com.example.s3.GeneratePresignedUrlAndPutFileWithQueryParams;
import com.example.s3.GeneratePresignedUrlAndUploadObject;
import com.example.s3.util.PresignUrlUtils;
import org.junit.jupiter.api.AfterAll;
@@ -16,6 +17,9 @@
import java.io.File;
import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@@ -23,7 +27,9 @@ class GeneratePresignedPutUrlTests {
private static final String BUCKET_NAME = "b-" + UUID.randomUUID();
private static final String KEY_NAME = "k-" + UUID.randomUUID();
private static final S3Client s3Client = S3Client.create();
- private static final Map METADATA = Map.of("meta1", "value1");
+ private static final String METADATA_KEY = "meta1";
+ private static final String METADATA_VALUE = "value1";
+ private static final Map METADATA = Map.of(METADATA_KEY, METADATA_VALUE);
private static final File PDF_FILE = GeneratePresignedUrlAndPutFileWithMetadata
.getFileForForClasspathResource("multipartUploadFiles/s3-userguide.pdf");
@@ -51,13 +57,63 @@ void testCreatePresignedUrlForPutString() {
Assertions.assertTrue(objectExists());
}
+ @Test
+ @Tag("IntegrationTest")
+ void test_create_presigned_url_using_query_params_does_not_add_to_signed_headers() {
+ Map queryParams = Map.of(
+ "x-amz-meta-author", "Bob",
+ "x-amz-meta-version", "1.0.0.0",
+ "x-amz-acl", "private",
+ "x-amz-server-side-encryption", "AES256"
+ );
+
+ GeneratePresignedUrlAndPutFileWithQueryParams presign = new GeneratePresignedUrlAndPutFileWithQueryParams();
+ String presignedUrl = presign.createPresignedUrl(BUCKET_NAME, KEY_NAME, queryParams);
+
+ Map queryStringMap = parseQueryString(presignedUrl);
+ Assertions.assertFalse(queryStringMap.get("X-Amz-SignedHeaders").contains("x-amz-meta-author"));
+ Assertions.assertFalse(queryStringMap.get("X-Amz-SignedHeaders").contains("x-amz-server-side-encryption"));
+ }
+
+ @Test
+ @Tag("IntegrationTest")
+ void test_create_presigned_url_using_query_params_works() {
+ Map queryParams = Map.of(
+ "x-amz-meta-author", "Bob",
+ "x-amz-meta-version", "1.0.0.0",
+ "x-amz-server-side-encryption", "AES256"
+ );
+
+ GeneratePresignedUrlAndPutFileWithQueryParams classUnderTest = new GeneratePresignedUrlAndPutFileWithQueryParams();
+ String presignedUrl = classUnderTest.createPresignedUrl(BUCKET_NAME, KEY_NAME, queryParams);
+
+ classUnderTest.useSdkHttpClientToPut(presignedUrl, PDF_FILE);
+
+ try (S3Client s3Client = S3Client.create()){
+ s3Client.getObject(builder -> {
+ builder.bucket(BUCKET_NAME);
+ builder.key(KEY_NAME);
+ builder.build();
+ }, (response, stream) -> {
+ stream.abort();
+ Assertions.assertEquals("Bob", response.metadata().get("author"));
+ Assertions.assertEquals("1.0.0.0", response.metadata().get("version"));
+ Assertions.assertEquals("AES256", response.serverSideEncryptionAsString());
+ return null;
+ });
+ }
+ }
+
@Test
@Tag("IntegrationTest")
void testCreatePresignedUrlForPut() {
GeneratePresignedUrlAndPutFileWithMetadata presignInstanceUnderTest = new GeneratePresignedUrlAndPutFileWithMetadata();
final String presignedUrlString = presignInstanceUnderTest.createPresignedUrl(BUCKET_NAME, KEY_NAME, METADATA);
- Assertions.assertTrue(presignedUrlString.contains("meta1"));
+ final Map queryParamsMap = parseQueryString(presignedUrlString);
+ // Assert that the metadata key, but not the value, is in the signed headers.
+ Assertions.assertTrue(queryParamsMap.get("X-Amz-SignedHeaders").contains("x-amz-meta-" + METADATA_KEY));
+ Assertions.assertFalse(queryParamsMap.get("X-Amz-SignedHeaders").contains("x-amz-meta-" + METADATA_VALUE));
}
@Test
@@ -106,4 +162,47 @@ private static Boolean objectHasMetadata() {
// The SDK strips off the leading "x-amz-meta-" from the metadata key.
return response.metadata().get("meta1").equals(GeneratePresignedPutUrlTests.METADATA.get("meta1"));
}
+
+ public static Map parseQueryString(String queryString) {
+ Map params = new HashMap<>();
+
+ // Return empty map for null or empty input
+ if (queryString == null || queryString.isEmpty()) {
+ return params;
+ }
+
+ // Split the query string into key-value pairs
+ String[] pairs = queryString.split("&");
+
+ for (String pair : pairs) {
+ // Find the separator between key and value
+ int separatorIndex = pair.indexOf("=");
+
+ // Handle key-only parameters (no value)
+ if (separatorIndex <= 0) {
+ params.put(URLDecoder.decode(pair, StandardCharsets.UTF_8), null);
+ continue;
+ }
+
+ // Extract and decode the key
+ String key = URLDecoder.decode(
+ pair.substring(0, separatorIndex),
+ StandardCharsets.UTF_8
+ );
+
+ // Extract and decode the value if it exists
+ String value = null;
+ if (pair.length() > separatorIndex + 1) {
+ value = URLDecoder.decode(
+ pair.substring(separatorIndex + 1),
+ StandardCharsets.UTF_8
+ );
+ }
+
+ params.put(key, value);
+ }
+
+ return params;
+ }
+
}
\ No newline at end of file
diff --git a/php/example_code/s3/express/README.md b/php/example_code/s3/express/README.md
index 96231648ecd..e1caa09d375 100644
--- a/php/example_code/s3/express/README.md
+++ b/php/example_code/s3/express/README.md
@@ -52,14 +52,14 @@ Code examples that show you how to perform the essential operations within a ser
This example shows you how to do the following:
-- Set up a VPC and VPC Endpoint
-- Set up the Policies, Roles, and User to work with S3 directory buckets and the S3 Express One Zone storage class
-- Create two S3 Clients
-- Create two buckets
-- Create an object and copy it over
-- Demonstrate performance difference
-- Populate the buckets to show the lexicographical difference
-- Prompt the user to see if they want to clean up the resources
+- Set up a VPC and VPC Endpoint.
+- Set up the Policies, Roles, and User to work with S3 directory buckets and the S3 Express One Zone storage class.
+- Create two S3 Clients.
+- Create two buckets.
+- Create an object and copy it over.
+- Demonstrate performance difference.
+- Populate the buckets to show the lexicographical difference.
+- Prompt the user to see if they want to clean up the resources.
diff --git a/swift/example_code/s3/presigned-urls/Sources/presigned-upload/entry.swift b/swift/example_code/s3/presigned-urls/Sources/presigned-upload/entry.swift
index 824464398e1..2ed004f135d 100644
--- a/swift/example_code/s3/presigned-urls/Sources/presigned-upload/entry.swift
+++ b/swift/example_code/s3/presigned-urls/Sources/presigned-upload/entry.swift
@@ -171,7 +171,6 @@ struct ExampleCommand: ParsableCommand {
} catch {
throw TransferError.signingError
}
- // snippet-end:[swift.s3.presigned.presign-PutObject-advanced]
// Send the HTTP request and upload the file to Amazon S3.
@@ -180,6 +179,7 @@ struct ExampleCommand: ParsableCommand {
request.body = .bytes(fileData)
_ = try await HTTPClient.shared.execute(request, timeout: .seconds(5*60))
+ // snippet-end:[swift.s3.presigned.presign-PutObject-advanced]
print("Uploaded (presigned) \(sourcePath) to \(bucket)/\(fileName).")
}