本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
使用 Amazon S3 預先簽章URLs
預先簽章URLs 提供私有 S3 物件的暫時存取權,而不需要使用者擁有 AWS 登入資料或許可。
例如,假設 Alice 可以存取 S3 物件,而且她想要暫時與 Bob 共用該物件的存取權。Alice 可以產生預先簽署的 GET 請求來與 Bob 共用,以便他可以下載物件,而無需存取 Alice 的登入資料。您可以為 HTTP GET 產生預先簽章URLs,並為 HTTP PUT 請求產生預先簽章的 URL。
產生物件的預先簽章 URL,然後下載它 (GET 請求)
下列範例包含兩個部分。
-
第 1 部分:Alice 會產生物件的預先簽章 URL。
-
第 2 部分:Bob 使用預先簽章的 URL 下載物件。
第 1 部分:產生 URL
Alice 在 S3 儲存貯體中已有物件。她使用以下程式碼來產生 URL 字串,以供 Bob 在後續 GET 請求中使用。
import com.example.s3.util.PresignUrlUtils; import org.slf4j.Logger; 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.GetObjectRequest; import software.amazon.awssdk.services.s3.model.S3Exception; import software.amazon.awssdk.services.s3.presigner.S3Presigner; import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest; import software.amazon.awssdk.utils.IoUtils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URISyntaxException; import java.net.URL; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.file.Paths; import java.time.Duration; import java.util.UUID;
/* Create a pre-signed URL to download an object in a subsequent GET request. */ public String createPresignedGetUrl(String bucketName, String keyName) { try (S3Presigner presigner = S3Presigner.create()) { GetObjectRequest objectRequest = GetObjectRequest.builder() .bucket(bucketName) .key(keyName) .build(); GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder() .signatureDuration(Duration.ofMinutes(10)) // The URL will expire in 10 minutes. .getObjectRequest(objectRequest) .build(); PresignedGetObjectRequest presignedRequest = presigner.presignGetObject(presignRequest); logger.info("Presigned URL: [{}]", presignedRequest.url().toString()); logger.info("HTTP method: [{}]", presignedRequest.httpRequest().method()); return presignedRequest.url().toExternalForm(); } }
第 2 部分:下載 物件
Bob 使用下列其中一個程式碼選項來下載物件。或者,他可以使用瀏覽器來執行 GET 請求。
/* Use the JDK HttpURLConnection (since v1.1) class to do the download. */ public byte[] useHttpUrlConnectionToGet(String presignedUrlString) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array. try { URL presignedUrl = new URL(presignedUrlString); HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection(); connection.setRequestMethod("GET"); // Download the result of executing the request. try (InputStream content = connection.getInputStream()) { IoUtils.copy(content, byteArrayOutputStream); } logger.info("HTTP response code is " + connection.getResponseCode()); } catch (S3Exception | IOException e) { logger.error(e.getMessage(), e); } return byteArrayOutputStream.toByteArray(); }
/* Use the JDK HttpClient (since v11) class to do the download. */ public byte[] useHttpClientToGet(String presignedUrlString) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array. HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(); HttpClient httpClient = HttpClient.newHttpClient(); try { URL presignedUrl = new URL(presignedUrlString); HttpResponse<InputStream> response = httpClient.send(requestBuilder .uri(presignedUrl.toURI()) .GET() .build(), HttpResponse.BodyHandlers.ofInputStream()); IoUtils.copy(response.body(), byteArrayOutputStream); logger.info("HTTP response code is " + response.statusCode()); } catch (URISyntaxException | InterruptedException | IOException e) { logger.error(e.getMessage(), e); } return byteArrayOutputStream.toByteArray(); }
/* Use the AWS SDK for Java SdkHttpClient class to do the download. */ public byte[] useSdkHttpClientToGet(String presignedUrlString) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array. try { URL presignedUrl = new URL(presignedUrlString); SdkHttpRequest request = SdkHttpRequest.builder() .method(SdkHttpMethod.GET) .uri(presignedUrl.toURI()) .build(); HttpExecuteRequest executeRequest = HttpExecuteRequest.builder() .request(request) .build(); try (SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) { HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call(); response.responseBody().ifPresentOrElse( abortableInputStream -> { try { IoUtils.copy(abortableInputStream, byteArrayOutputStream); } catch (IOException e) { throw new RuntimeException(e); } }, () -> logger.error("No response body.")); logger.info("HTTP Response code is {}", response.httpResponse().statusCode()); } } catch (URISyntaxException | IOException e) { logger.error(e.getMessage(), e); } return byteArrayOutputStream.toByteArray(); }
在 上 AWS SDK for Java 2.x 提供預先簽章的 URL 延伸S3AsyncClient, 會透過 SDK 非同步管道使用預先簽章的 URL 下載物件。使用 存取擴充功能S3AsyncClient.presignedUrlExtension()。此功能適用於 SDK 2.48.0 版及更新版本。
CRT 用戶端限制
AWS CRT 型 S3 用戶端 (S3AsyncClient.crtBuilder()) 目前不支援預先簽章的 URL 延伸。請改用 S3AsyncClient.builder()。
檢查總和模式需求
若要驗證資料完整性,請在產生預先簽章的 URL GetObjectRequest時,在 checksumMode(ChecksumMode.ENABLED)上設定 。在預先簽署時間指定檢查總和模式。您無法在稍後執行下載時新增它。當您使用S3AsyncClient預先簽章的 URL 延伸時,軟體開發套件會自動傳送所需的x-amz-checksum-mode標頭。
import java.nio.file.Paths; import software.amazon.awssdk.core.ResponseBytes; import software.amazon.awssdk.core.async.AsyncResponseTransformer; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3AsyncClient; import software.amazon.awssdk.services.s3.model.GetObjectResponse; import software.amazon.awssdk.services.s3.presignedurl.model.PresignedUrlDownloadRequest;
下載至檔案或位元組
大型檔案的分段下載
若要平行下載大型物件,請在用戶端multipartEnabled(true)上設定 :
使用 Amazon S3 Transfer Manager
透過 Amazon S3 Transfer Manager,您可以使用預先簽章URLs 透過 進行進度追蹤來下載物件TransferListener。
將s3-transfer-manager成品新增至您的專案:
<dependencyManagement> <dependencies> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>bom</artifactId> <version>2.48.01</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3-transfer-manager</artifactId> </dependency> </dependencies>
1 在 Maven Central Repository 上尋找最新的 BOM 版本
import java.nio.file.Paths; import software.amazon.awssdk.core.ResponseBytes; import software.amazon.awssdk.core.async.AsyncResponseTransformer; import software.amazon.awssdk.services.s3.model.GetObjectResponse; import software.amazon.awssdk.services.s3.presignedurl.model.PresignedUrlDownloadRequest; import software.amazon.awssdk.transfer.s3.S3TransferManager; import software.amazon.awssdk.transfer.s3.model.CompletedFileDownload; import software.amazon.awssdk.transfer.s3.model.Download; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadFileRequest; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadRequest; import software.amazon.awssdk.transfer.s3.model.PresignedFileDownload; import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;
不支援暫停和繼續
與一般 不同FileDownload, PresignedFileDownload 不支援暫停和繼續,因為預先簽章URLs 可能會在暫停傳輸期間過期。
如需 Amazon S3 Transfer Manager 的詳細資訊,包括組態選項和其他範例,請參閱 使用 Amazon S3 Transfer Manager 傳輸檔案和目錄。
產生上傳的預先簽章 URL,然後上傳檔案 (PUT 請求)
下列範例包含兩個部分。
-
第 1 部分:Alice 會產生預先簽章的 URL 來上傳物件。
-
第 2 部分:Bob 使用預先簽章的 URL 上傳檔案。
第 1 部分:產生 URL
Alice 已有 S3 儲存貯體。她使用以下程式碼來產生可在後續 PUT 請求中使用的 URL 字串。
import com.example.s3.util.PresignUrlUtils; import org.slf4j.Logger; 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.model.S3Exception; 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.io.OutputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URISyntaxException; import java.net.URL; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.Map; import java.util.UUID;
/* Create a presigned URL to use in a subsequent PUT request */ public String createPresignedUrl(String bucketName, String keyName, Map<String, String> metadata) { try (S3Presigner presigner = S3Presigner.create()) { PutObjectRequest objectRequest = PutObjectRequest.builder() .bucket(bucketName) .key(keyName) .metadata(metadata) .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(); } }
第 2 部分:上傳檔案物件
Bob 使用下列三個程式碼選項之一來上傳檔案。
/* Use the JDK HttpURLConnection (since v1.1) class to do the upload. */ public void useHttpUrlConnectionToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) { logger.info("Begin [{}] upload", fileToPut.toString()); try { URL presignedUrl = new URL(presignedUrlString); HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection(); connection.setDoOutput(true); metadata.forEach((k, v) -> connection.setRequestProperty("x-amz-meta-" + k, v)); connection.setRequestMethod("PUT"); OutputStream out = connection.getOutputStream(); try (RandomAccessFile file = new RandomAccessFile(fileToPut, "r"); FileChannel inChannel = file.getChannel()) { ByteBuffer buffer = ByteBuffer.allocate(8192); //Buffer size is 8k while (inChannel.read(buffer) > 0) { buffer.flip(); for (int i = 0; i < buffer.limit(); i++) { out.write(buffer.get()); } buffer.clear(); } } catch (IOException e) { logger.error(e.getMessage(), e); } out.close(); connection.getResponseCode(); logger.info("HTTP response code is " + connection.getResponseCode()); } catch (S3Exception | IOException e) { logger.error(e.getMessage(), e); } }
/* Use the JDK HttpClient (since v11) class to do the upload. */ public void useHttpClientToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) { logger.info("Begin [{}] upload", fileToPut.toString()); HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(); metadata.forEach((k, v) -> requestBuilder.header("x-amz-meta-" + k, v)); HttpClient httpClient = HttpClient.newHttpClient(); try { final HttpResponse<Void> response = httpClient.send(requestBuilder .uri(new URL(presignedUrlString).toURI()) .PUT(HttpRequest.BodyPublishers.ofFile(Path.of(fileToPut.toURI()))) .build(), HttpResponse.BodyHandlers.discarding()); logger.info("HTTP response code is " + response.statusCode()); } catch (URISyntaxException | InterruptedException | IOException e) { logger.error(e.getMessage(), e); } }
/* Use the AWS SDK for Java V2 SdkHttpClient class to do the upload. */ public void useSdkHttpClientToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) { logger.info("Begin [{}] upload", fileToPut.toString()); try { URL presignedUrl = new URL(presignedUrlString); SdkHttpRequest.Builder requestBuilder = SdkHttpRequest.builder() .method(SdkHttpMethod.PUT) .uri(presignedUrl.toURI()); // Add headers metadata.forEach((k, v) -> requestBuilder.putHeader("x-amz-meta-" + k, v)); // Finish building the request. 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); } }
包含請求欄位做為 URL 查詢參數
GetObjectRequestrequestPayer、acl、serverSideEncryption、 metadata和 storageClass 對應至 x-amz-*標頭。當您預先簽署設定其中一個欄位的請求時,簽章會涵蓋標頭。不過,S3Presigner
預先簽章 URL 的發起人必須與請求一起傳送相同的x-amz-*標頭。否則,Amazon S3 會傳回 SignatureDoesNotMatch。可以設定自訂標頭的 HTTP 用戶端會直接傳送這些標頭。例如,先前顯示的 HttpURLConnection、 HttpClient和 SdkHttpClient
若要讓 URL 在沒有額外標頭的情況下運作,請將 值傳遞為已簽章的查詢參數,而不是設定輸入欄位。在 AwsRequestOverrideConfiguration
使用申請者付款預先簽署 GET 請求
下列範例會為GetObjectRequest具有 請求者付款設定的 產生預先簽章的 URL。由於 值是查詢參數,任何呼叫者都可以使用 URL,而無需傳送 x-amz-request-payer標頭。
try (S3Presigner presigner = S3Presigner.create()) { GetObjectRequest getObjectRequest = GetObjectRequest.builder() .bucket("amzn-s3-demo-bucket") .key("example-key") .overrideConfiguration(o -> o.putRawQueryParameter("x-amz-request-payer", "requester")) .build(); GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder() .signatureDuration(Duration.ofMinutes(10)) .getObjectRequest(getObjectRequest) .build(); PresignedGetObjectRequest presigned = presigner.presignGetObject(presignRequest); logger.info("Pre-signed URL: [{}]", presigned.url()); // Confirm the URL requires no headers at request time: logger.info("Signed headers: {}", presigned.signedHeaders().keySet()); // [host] }
使用中繼資料預先簽署 PUT 請求
下列範例會為 產生預先簽章的 URLPutObjectRequest,其中包含兩個中繼資料值做為已簽章的查詢參數。由於這些值是查詢參數,任何呼叫者都可以使用 URL,而無需傳送x-amz-meta-*標頭。
try (S3Presigner presigner = S3Presigner.create()) { PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket("amzn-s3-demo-bucket") .key("example-key") .overrideConfiguration(o -> o .putRawQueryParameter("x-amz-meta-author", "alice") .putRawQueryParameter("x-amz-meta-purpose", "demo")) .build(); PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder() .signatureDuration(Duration.ofMinutes(10)) .putObjectRequest(putObjectRequest) .build(); PresignedPutObjectRequest presigned = presigner.presignPutObject(presignRequest); logger.info("Pre-signed URL: [{}]", presigned.url()); // Confirm the URL requires no headers at upload time: logger.info("Signed headers: {}", presigned.signedHeaders().keySet()); // [host] }
預先簽署 PUT 請求並上傳檔案
以下範例顯示完整的工作流程:使用您作為映射提供的任何x-amz-*名稱和值預先簽署 PUT 請求的方法,以及使用 上傳檔案的方法SdkHttpClient。由於這些值已經是已簽署 URL 的一部分,因此上傳方法不會設定任何x-amz-*標頭。相反地,先前的上傳範例會明確設定x-amz-meta-*標頭。
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;
產生 URL。
/** * 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<String, String> 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(); } }
使用 上傳檔案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); } }
檢查預先簽署的請求需要哪些標頭
若要檢查特定預先簽署請求需要發起人傳送的標頭,請在傳回的 PresignedRequestx-amz-*名稱都是發起人必須傳送的標頭。如果您想要從需求中移除標頭,請傳遞其值,putRawQueryParameter而不是設定輸入欄位。