diff --git a/sdk/storage/azure-storage-blob-cryptography/src/test/java/com/azure/storage/blob/specialized/cryptography/EncyptedBlockBlobAPITest.groovy b/sdk/storage/azure-storage-blob-cryptography/src/test/java/com/azure/storage/blob/specialized/cryptography/EncyptedBlockBlobAPITest.groovy index cd459de938f1..e50797614010 100644 --- a/sdk/storage/azure-storage-blob-cryptography/src/test/java/com/azure/storage/blob/specialized/cryptography/EncyptedBlockBlobAPITest.groovy +++ b/sdk/storage/azure-storage-blob-cryptography/src/test/java/com/azure/storage/blob/specialized/cryptography/EncyptedBlockBlobAPITest.groovy @@ -1369,6 +1369,9 @@ class EncyptedBlockBlobAPITest extends APISpec { @Unroll def "Encryption uploadIS numBlocks"() { setup: + if (numBlocks > 0 && !liveMode()) { + return // Multipart upload paths use randomly generated block ids that can't be recorded and played back. + } def randomData = getRandomByteArray(size) def input = new ByteArrayInputStream(randomData) diff --git a/sdk/storage/azure-storage-blob-cryptography/src/test/resources/session-records/EncyptedBlockBlobAPITestdownloadsnapshot.json b/sdk/storage/azure-storage-blob-cryptography/src/test/resources/session-records/EncyptedBlockBlobAPITestdownloadsnapshot.json index 5351cb9c2fc7..6a82e776d251 100644 --- a/sdk/storage/azure-storage-blob-cryptography/src/test/resources/session-records/EncyptedBlockBlobAPITestdownloadsnapshot.json +++ b/sdk/storage/azure-storage-blob-cryptography/src/test/resources/session-records/EncyptedBlockBlobAPITestdownloadsnapshot.json @@ -1,174 +1,196 @@ { "networkCallRecords" : [ { "Method" : "PUT", - "Uri" : "https://gaprastg71.blob.core.windows.net/jtcdownloadsnapshot078967247abacd89ab40e3a17?restype=container", + "Uri" : "https://REDACTED.blob.core.windows.net/jtcdownloadsnapshot009723d0eb0f61930443d7bf3?restype=container", "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-storage-blob/12.4.0-beta.1 (11.0.4; Windows 10 10.0)", - "x-ms-client-request-id" : "f54e2494-0bd5-4001-a768-656ab6c89065" + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "444a47f0-a18f-4ed6-9372-844188306f22" }, "Response" : { - "x-ms-version" : "2019-02-02", + "x-ms-version" : "2019-07-07", "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", - "ETag" : "0x8D7A11867F18EBE", - "Last-Modified" : "Fri, 24 Jan 2020 21:57:29 GMT", + "ETag" : "0x8D801C26340D9BD", + "Last-Modified" : "Tue, 26 May 2020 22:16:07 GMT", "retry-after" : "0", "Content-Length" : "0", "StatusCode" : "201", - "x-ms-request-id" : "bd0cd018-a01e-0020-2f01-d3e99c000000", - "Date" : "Fri, 24 Jan 2020 21:57:28 GMT", - "x-ms-client-request-id" : "f54e2494-0bd5-4001-a768-656ab6c89065" + "x-ms-request-id" : "3752f172-001e-013a-45ab-3314eb000000", + "Date" : "Tue, 26 May 2020 22:16:06 GMT", + "x-ms-client-request-id" : "444a47f0-a18f-4ed6-9372-844188306f22" }, "Exception" : null }, { "Method" : "HEAD", - "Uri" : "https://gaprastg71.blob.core.windows.net/jtcdownloadsnapshot078967247abacd89ab40e3a17/javablobdownloadsnapshot343745ec50c71b14c94c66", + "Uri" : "https://REDACTED.blob.core.windows.net/jtcdownloadsnapshot009723d0eb0f61930443d7bf3/javablobdownloadsnapshot34979421d0ad9f047a4ee6", "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-storage-blob/12.4.0-beta.1 (11.0.4; Windows 10 10.0)", - "x-ms-client-request-id" : "e3f035d2-46d9-4595-985f-352334ce68ea" + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "77de0445-fb2e-415c-9f74-54b1b07f6076" }, "Response" : { - "x-ms-version" : "2019-02-02", + "x-ms-version" : "2019-07-07", "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", - "Vary" : "Origin", "x-ms-error-code" : "BlobNotFound", "retry-after" : "0", "StatusCode" : "404", - "x-ms-request-id" : "bd0cd01a-a01e-0020-3001-d3e99c000000", - "Date" : "Fri, 24 Jan 2020 21:57:28 GMT", - "x-ms-client-request-id" : "e3f035d2-46d9-4595-985f-352334ce68ea" + "x-ms-request-id" : "c1566cd9-a01e-005e-68ab-33e21e000000", + "Date" : "Tue, 26 May 2020 22:16:07 GMT", + "x-ms-client-request-id" : "77de0445-fb2e-415c-9f74-54b1b07f6076" }, "Exception" : null }, { "Method" : "PUT", - "Uri" : "https://gaprastg71.blob.core.windows.net/jtcdownloadsnapshot078967247abacd89ab40e3a17/javablobdownloadsnapshot343745ec50c71b14c94c66", + "Uri" : "https://REDACTED.blob.core.windows.net/jtcdownloadsnapshot009723d0eb0f61930443d7bf3/javablobdownloadsnapshot34979421d0ad9f047a4ee6", "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-storage-blob/12.4.0-beta.1 (11.0.4; Windows 10 10.0)", - "x-ms-client-request-id" : "b9c705f7-8889-4a6a-b325-ffab6d6c2376", + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "459e07c6-c230-402f-93d7-461976d47543", "Content-Type" : "application/octet-stream" }, "Response" : { - "x-ms-version" : "2019-02-02", + "x-ms-version" : "2019-07-07", "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", - "x-ms-content-crc64" : "KDwsypnglr8=", - "Last-Modified" : "Fri, 24 Jan 2020 21:57:29 GMT", + "x-ms-content-crc64" : "jLe4z3h6oPg=", + "Last-Modified" : "Tue, 26 May 2020 22:16:08 GMT", "retry-after" : "0", "StatusCode" : "201", "x-ms-request-server-encrypted" : "true", - "Date" : "Fri, 24 Jan 2020 21:57:28 GMT", - "Content-MD5" : "QjriprzozKnKgklcineviQ==", - "ETag" : "0x8D7A118680A0C57", + "Date" : "Tue, 26 May 2020 22:16:07 GMT", + "Content-MD5" : "kfvMU+iKpN37pAOKnFyyKw==", + "ETag" : "0x8D801C263ADBBA0", "Content-Length" : "0", - "x-ms-request-id" : "bd0cd01b-a01e-0020-3101-d3e99c000000", - "x-ms-client-request-id" : "b9c705f7-8889-4a6a-b325-ffab6d6c2376" + "x-ms-request-id" : "4dfc0c89-601e-00ad-76ab-333177000000", + "x-ms-client-request-id" : "459e07c6-c230-402f-93d7-461976d47543" }, "Exception" : null }, { "Method" : "GET", - "Uri" : "https://gaprastg71.blob.core.windows.net/jtcdownloadsnapshot078967247abacd89ab40e3a17/javablobdownloadsnapshot343745ec50c71b14c94c66", + "Uri" : "https://REDACTED.blob.core.windows.net/jtcdownloadsnapshot009723d0eb0f61930443d7bf3/javablobdownloadsnapshot34979421d0ad9f047a4ee6", "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-storage-blob/12.4.0-beta.1 (11.0.4; Windows 10 10.0)", - "x-ms-client-request-id" : "a42aa895-86c8-4f89-b76a-f8b016107504" + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "e101483e-b32d-4483-9661-e271dddbcf45" }, "Response" : { - "x-ms-version" : "2019-02-02", + "x-ms-version" : "2019-07-07", "x-ms-lease-status" : "unlocked", "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", "x-ms-lease-state" : "available", - "Last-Modified" : "Fri, 24 Jan 2020 21:57:29 GMT", + "Last-Modified" : "Tue, 26 May 2020 22:16:08 GMT", "retry-after" : "0", "StatusCode" : "200", - "Date" : "Fri, 24 Jan 2020 21:57:28 GMT", + "Date" : "Tue, 26 May 2020 22:16:08 GMT", "x-ms-blob-type" : "BlockBlob", - "Content-MD5" : "QjriprzozKnKgklcineviQ==", + "Content-MD5" : "kfvMU+iKpN37pAOKnFyyKw==", "Accept-Ranges" : "bytes", - "x-ms-meta-encryptiondata" : "{\"EncryptionMode\":\"FullBlob\",\"WrappedContentKey\":{\"KeyId\":\"keyId\",\"EncryptedKey\":\"JR9D+eWou6/23ubDQRjuBh3Z8drGdIsZNHMvKueWF1U=\",\"Algorithm\":\"keyWrapAlgorithm\"},\"EncryptionAgent\":{\"Protocol\":\"1.0\",\"EncryptionAlgorithm\":\"AES_CBC_256\"},\"ContentEncryptionIV\":\"aw0+35MY3GwM7Nh9wUtr7Q==\",\"KeyWrappingMetadata\":{\"EncryptionLibrary\":\"JavaTrack212.4.0-beta.1\"}}", + "x-ms-meta-encryptiondata" : "{\"EncryptionMode\":\"FullBlob\",\"WrappedContentKey\":{\"KeyId\":\"keyId\",\"EncryptedKey\":\"pXyp7xrBUiysWoQWIDSoOOl0BfNKqXQze3e6+tHY/oM=\",\"Algorithm\":\"keyWrapAlgorithm\"},\"EncryptionAgent\":{\"Protocol\":\"1.0\",\"EncryptionAlgorithm\":\"AES_CBC_256\"},\"ContentEncryptionIV\":\"Xu45Y5GrbtVMg3qNIA0RAw==\",\"KeyWrappingMetadata\":{\"EncryptionLibrary\":\"JavaTrack212.7.0-beta.1\"}}", "x-ms-server-encrypted" : "true", - "ETag" : "0x8D7A118680A0C57", - "Vary" : "Origin", - "x-ms-creation-time" : "Fri, 24 Jan 2020 21:57:29 GMT", + "ETag" : "0x8D801C263ADBBA0", + "x-ms-creation-time" : "Tue, 26 May 2020 22:16:08 GMT", "Content-Length" : "16", - "x-ms-request-id" : "bd0cd01d-a01e-0020-3201-d3e99c000000", - "Body" : "[80, 94, -47, -63, 2, -30, 36, 4, -61, -55, 64, 62, 112, -92, 10, -67]", - "x-ms-client-request-id" : "a42aa895-86c8-4f89-b76a-f8b016107504", + "x-ms-request-id" : "f5141876-a01e-0099-17ab-339edf000000", + "Body" : "[62, -93, -43, 44, 108, 4, -4, 46, -62, -82, -34, 0, 33, 64, 105, 118]", + "x-ms-client-request-id" : "e101483e-b32d-4483-9661-e271dddbcf45", "Content-Type" : "application/octet-stream" }, "Exception" : null }, { "Method" : "PUT", - "Uri" : "https://gaprastg71.blob.core.windows.net/jtcdownloadsnapshot078967247abacd89ab40e3a17/javablobdownloadsnapshot343745ec50c71b14c94c66?comp=snapshot", + "Uri" : "https://REDACTED.blob.core.windows.net/jtcdownloadsnapshot009723d0eb0f61930443d7bf3/javablobdownloadsnapshot34979421d0ad9f047a4ee6?comp=snapshot", "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-storage-blob/12.4.0-beta.1 (11.0.4; Windows 10 10.0)", - "x-ms-client-request-id" : "e7cc0dad-b7ad-42e9-8e09-562893cf3e77" + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "c2d60781-f1fe-4e47-b906-c99201b812cb" }, "Response" : { - "x-ms-version" : "2019-02-02", - "x-ms-snapshot" : "2020-01-24T21:57:29.4860224Z", + "x-ms-version" : "2019-07-07", + "x-ms-snapshot" : "2020-05-26T22:16:08.4992795Z", "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", - "ETag" : "0x8D7A118680A0C57", - "Last-Modified" : "Fri, 24 Jan 2020 21:57:29 GMT", + "ETag" : "0x8D801C263ADBBA0", + "Last-Modified" : "Tue, 26 May 2020 22:16:08 GMT", "retry-after" : "0", "Content-Length" : "0", "StatusCode" : "201", - "x-ms-request-id" : "bd0cd01e-a01e-0020-3301-d3e99c000000", + "x-ms-request-id" : "dd1e5e4b-601e-00c0-51ab-339b59000000", "x-ms-request-server-encrypted" : "false", - "Date" : "Fri, 24 Jan 2020 21:57:28 GMT", - "x-ms-client-request-id" : "e7cc0dad-b7ad-42e9-8e09-562893cf3e77" + "Date" : "Tue, 26 May 2020 22:16:07 GMT", + "x-ms-client-request-id" : "c2d60781-f1fe-4e47-b906-c99201b812cb" + }, + "Exception" : null + }, { + "Method" : "PUT", + "Uri" : "https://REDACTED.blob.core.windows.net/jtcdownloadsnapshot009723d0eb0f61930443d7bf3/javablobdownloadsnapshot34979421d0ad9f047a4ee6", + "Headers" : { + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "f7fd1cc1-32c1-4181-aad1-e8511748817d", + "Content-Type" : "application/octet-stream" + }, + "Response" : { + "x-ms-version" : "2019-07-07", + "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-content-crc64" : "/bw8GRgncJ0=", + "Last-Modified" : "Tue, 26 May 2020 22:16:08 GMT", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 26 May 2020 22:16:08 GMT", + "Content-MD5" : "pZctVoTDGASGUvVwUUrG8w==", + "ETag" : "0x8D801C2641017FE", + "Content-Length" : "0", + "x-ms-request-id" : "7a88316d-e01e-0034-27ab-33beb5000000", + "x-ms-client-request-id" : "f7fd1cc1-32c1-4181-aad1-e8511748817d" }, "Exception" : null }, { "Method" : "GET", - "Uri" : "https://gaprastg71.blob.core.windows.net/jtcdownloadsnapshot078967247abacd89ab40e3a17/javablobdownloadsnapshot343745ec50c71b14c94c66?snapshot=2020-01-24T21%3a57%3a29.4860224Z", + "Uri" : "https://REDACTED.blob.core.windows.net/jtcdownloadsnapshot009723d0eb0f61930443d7bf3/javablobdownloadsnapshot34979421d0ad9f047a4ee6?snapshot=2020-05-26T22%3A16%3A08.4992795Z", "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-storage-blob/12.4.0-beta.1 (11.0.4; Windows 10 10.0)", - "x-ms-client-request-id" : "faff960f-56af-4166-8733-960643d88d10" + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "03dd3ae8-3318-4703-8594-65c5d523b455" }, "Response" : { - "x-ms-version" : "2019-02-02", + "x-ms-version" : "2019-07-07", "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", - "Last-Modified" : "Fri, 24 Jan 2020 21:57:29 GMT", + "Last-Modified" : "Tue, 26 May 2020 22:16:08 GMT", "retry-after" : "0", "StatusCode" : "200", - "Date" : "Fri, 24 Jan 2020 21:57:28 GMT", + "Date" : "Tue, 26 May 2020 22:16:08 GMT", "x-ms-blob-type" : "BlockBlob", - "Content-MD5" : "QjriprzozKnKgklcineviQ==", + "Content-MD5" : "kfvMU+iKpN37pAOKnFyyKw==", "Accept-Ranges" : "bytes", - "x-ms-meta-encryptiondata" : "{\"EncryptionMode\":\"FullBlob\",\"WrappedContentKey\":{\"KeyId\":\"keyId\",\"EncryptedKey\":\"JR9D+eWou6/23ubDQRjuBh3Z8drGdIsZNHMvKueWF1U=\",\"Algorithm\":\"keyWrapAlgorithm\"},\"EncryptionAgent\":{\"Protocol\":\"1.0\",\"EncryptionAlgorithm\":\"AES_CBC_256\"},\"ContentEncryptionIV\":\"aw0+35MY3GwM7Nh9wUtr7Q==\",\"KeyWrappingMetadata\":{\"EncryptionLibrary\":\"JavaTrack212.4.0-beta.1\"}}", + "x-ms-meta-encryptiondata" : "{\"EncryptionMode\":\"FullBlob\",\"WrappedContentKey\":{\"KeyId\":\"keyId\",\"EncryptedKey\":\"pXyp7xrBUiysWoQWIDSoOOl0BfNKqXQze3e6+tHY/oM=\",\"Algorithm\":\"keyWrapAlgorithm\"},\"EncryptionAgent\":{\"Protocol\":\"1.0\",\"EncryptionAlgorithm\":\"AES_CBC_256\"},\"ContentEncryptionIV\":\"Xu45Y5GrbtVMg3qNIA0RAw==\",\"KeyWrappingMetadata\":{\"EncryptionLibrary\":\"JavaTrack212.7.0-beta.1\"}}", "x-ms-server-encrypted" : "true", - "ETag" : "0x8D7A118680A0C57", - "Vary" : "Origin", - "x-ms-creation-time" : "Fri, 24 Jan 2020 21:57:29 GMT", + "ETag" : "0x8D801C263ADBBA0", + "x-ms-creation-time" : "Tue, 26 May 2020 22:16:08 GMT", "Content-Length" : "16", - "x-ms-request-id" : "bd0cd021-a01e-0020-3401-d3e99c000000", - "Body" : "[80, 94, -47, -63, 2, -30, 36, 4, -61, -55, 64, 62, 112, -92, 10, -67]", - "x-ms-client-request-id" : "faff960f-56af-4166-8733-960643d88d10", + "x-ms-request-id" : "ec0e1ac7-201e-000b-3fab-330969000000", + "Body" : "[62, -93, -43, 44, 108, 4, -4, 46, -62, -82, -34, 0, 33, 64, 105, 118]", + "x-ms-client-request-id" : "03dd3ae8-3318-4703-8594-65c5d523b455", "Content-Type" : "application/octet-stream" }, "Exception" : null }, { "Method" : "DELETE", - "Uri" : "https://gaprastg71.blob.core.windows.net/jtcdownloadsnapshot078967247abacd89ab40e3a17?restype=container", + "Uri" : "https://REDACTED.blob.core.windows.net/jtcdownloadsnapshot009723d0eb0f61930443d7bf3?restype=container", "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-storage-blob/12.4.0-beta.1 (11.0.4; Windows 10 10.0)", - "x-ms-client-request-id" : "55ca069a-0055-422a-9300-bac98188fb43" + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "08ff37ec-9738-437e-9fa7-24871121fb41" }, "Response" : { - "x-ms-version" : "2019-02-02", + "x-ms-version" : "2019-07-07", "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", "retry-after" : "0", "Content-Length" : "0", "StatusCode" : "202", - "x-ms-request-id" : "bd0cd022-a01e-0020-3501-d3e99c000000", - "Date" : "Fri, 24 Jan 2020 21:57:28 GMT", - "x-ms-client-request-id" : "55ca069a-0055-422a-9300-bac98188fb43" + "x-ms-request-id" : "13e7c197-501e-0122-48ab-33397e000000", + "Date" : "Tue, 26 May 2020 22:16:08 GMT", + "x-ms-client-request-id" : "08ff37ec-9738-437e-9fa7-24871121fb41" }, "Exception" : null } ], - "variables" : [ "c44034ac-e836-483e-a79f-7f19eed8102a", "jtcdownloadsnapshot078967247abacd89ab40e3a17", "javablobdownloadsnapshot157027e24a3c9edde444fe", "javablobdownloadsnapshot2130572b0c4f5db9a247f1", "javablobdownloadsnapshot343745ec50c71b14c94c66" ] + "variables" : [ "1c84cc40-c4a1-463d-bb1b-c4baf026ecf7", "jtcdownloadsnapshot009723d0eb0f61930443d7bf3", "javablobdownloadsnapshot186183bfd1e0c3422044c1", "javablobdownloadsnapshot25344450812cd6a531487e", "javablobdownloadsnapshot34979421d0ad9f047a4ee6" ] } \ No newline at end of file diff --git a/sdk/storage/azure-storage-blob/CHANGELOG.md b/sdk/storage/azure-storage-blob/CHANGELOG.md index 548b137d4b19..685c8b339e64 100644 --- a/sdk/storage/azure-storage-blob/CHANGELOG.md +++ b/sdk/storage/azure-storage-blob/CHANGELOG.md @@ -1,6 +1,7 @@ # Release History ## 12.7.0-beta.1 (Unreleased) +- Added an overload to BlobClient.upload which returns a BlockBlobItem containing the properties returned by the service upon blob creation. - Fixed a bug that caused auth failures when constructing a client to a secondary endpoint using token auth. ## 12.6.1 (2020-05-06) diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobAsyncClient.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobAsyncClient.java index 83f6c1b7ecd4..cf6ed960aa50 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobAsyncClient.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobAsyncClient.java @@ -384,7 +384,6 @@ private Mono> uploadInChunks(BlockBlobAsyncClient blockB final String blockId = Base64.getEncoder().encodeToString( UUID.randomUUID().toString().getBytes(UTF_8)); - return blockBlobAsyncClient.stageBlockWithResponse(blockId, progressData, buffer.remaining(), null, requestConditions.getLeaseId()) // We only care about the stageBlock insofar as it was successful, diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobClient.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobClient.java index 5e7f5d5becf5..38066d0e7e0e 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobClient.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobClient.java @@ -4,27 +4,28 @@ package com.azure.storage.blob; import com.azure.core.annotation.ServiceClient; +import com.azure.core.http.rest.Response; import com.azure.core.util.Context; +import com.azure.core.util.FluxUtil; import com.azure.core.util.logging.ClientLogger; import com.azure.storage.blob.implementation.util.ModelHelper; import com.azure.storage.blob.models.AccessTier; +import com.azure.storage.blob.models.BlobParallelUploadOptions; import com.azure.storage.blob.models.BlobRequestConditions; import com.azure.storage.blob.models.BlobHttpHeaders; -import com.azure.storage.blob.models.BlobStorageException; +import com.azure.storage.blob.models.BlockBlobItem; import com.azure.storage.blob.models.ParallelTransferOptions; import com.azure.storage.blob.specialized.AppendBlobClient; import com.azure.storage.blob.specialized.BlobClientBase; -import com.azure.storage.blob.specialized.BlobOutputStream; import com.azure.storage.blob.specialized.BlockBlobClient; import com.azure.storage.blob.specialized.PageBlobClient; import com.azure.storage.blob.specialized.SpecializedBlobClientBuilder; +import com.azure.storage.common.Utility; import com.azure.storage.common.implementation.Constants; import com.azure.storage.common.implementation.StorageImplUtils; import com.azure.storage.common.implementation.UploadUtils; import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; -import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.time.Duration; @@ -169,29 +170,39 @@ public void upload(InputStream data, long length, boolean overwrite) { public void uploadWithResponse(InputStream data, long length, ParallelTransferOptions parallelTransferOptions, BlobHttpHeaders headers, Map metadata, AccessTier tier, BlobRequestConditions requestConditions, Duration timeout, Context context) { + uploadWithResponse(data, length, new BlobParallelUploadOptions() + .setParallelTransferOptions(parallelTransferOptions).setHeaders(headers).setMetadata(metadata).setTier(tier) + .setRequestConditions(requestConditions), timeout, context); + } + + /** + * Creates a new blob, or updates the content of an existing blob. + *

+ * To avoid overwriting, pass "*" to {@link BlobRequestConditions#setIfNoneMatch(String)}. + * + * @param data The data to write to the blob. + * @param length The exact length of the data. It is important that this value match precisely the length of the + * data provided in the {@link InputStream}. + * @param options {@link BlobParallelUploadOptions} + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param context Additional context that is passed through the Http pipeline during the service call. + * @return Information about the uploaded block blob. + */ + public Response uploadWithResponse(InputStream data, long length, BlobParallelUploadOptions options, + Duration timeout, Context context) { + BlobParallelUploadOptions blobParallelUploadOptions = options == null ? new BlobParallelUploadOptions() + : options; final ParallelTransferOptions validatedParallelTransferOptions = - ModelHelper.populateAndApplyDefaults(parallelTransferOptions); - Mono upload = Mono.fromCallable(() -> { - try { - // BlobOutputStream will internally handle the decision for single-shot or multi-part upload. - BlobOutputStream blobOutputStream = BlobOutputStream.blockBlobOutputStream(client, - validatedParallelTransferOptions, headers, metadata, tier, requestConditions, context); - StorageImplUtils.copyToOutputStream(data, length, blobOutputStream); - blobOutputStream.close(); - return null; - } catch (IOException e) { - Throwable cause = e.getCause(); - if (cause instanceof BlobStorageException) { - throw logger.logExceptionAsError((BlobStorageException) cause); - } else { - throw logger.logExceptionAsError(new UncheckedIOException(e)); - } - } - // Subscribing has to happen on a different thread for the timeout to happen properly. - }).subscribeOn(Schedulers.elastic()); + ModelHelper.populateAndApplyDefaults(blobParallelUploadOptions.getParallelTransferOptions()); + + Mono> upload = client.uploadWithResponse(Utility.convertStreamToByteBuffer(data, length, + validatedParallelTransferOptions.getBlockSize()), validatedParallelTransferOptions, + blobParallelUploadOptions.getHeaders(), blobParallelUploadOptions.getMetadata(), + blobParallelUploadOptions.getTier(), blobParallelUploadOptions.getRequestConditions()) + .subscriberContext(FluxUtil.toReactorContext(context)); try { - StorageImplUtils.blockWithOptionalTimeout(upload, timeout); + return StorageImplUtils.blockWithOptionalTimeout(upload, timeout); } catch (UncheckedIOException e) { throw logger.logExceptionAsError(e); } diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobParallelUploadOptions.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobParallelUploadOptions.java new file mode 100644 index 000000000000..33097891bd41 --- /dev/null +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobParallelUploadOptions.java @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.blob.models; + +import com.azure.core.annotation.Fluent; + +import java.util.Map; + +/** + * Extended options that may be passed when uploading a Block Blob in parallel. + */ +@Fluent +public class BlobParallelUploadOptions { + private ParallelTransferOptions parallelTransferOptions; + private BlobHttpHeaders headers; + private Map metadata; + private AccessTier tier; + private BlobRequestConditions requestConditions; + + /** + * @return {@link ParallelTransferOptions} + */ + public ParallelTransferOptions getParallelTransferOptions() { + return parallelTransferOptions; + } + + /** + * @param parallelTransferOptions {@link ParallelTransferOptions} + * @return The updated options. + */ + public BlobParallelUploadOptions setParallelTransferOptions(ParallelTransferOptions parallelTransferOptions) { + this.parallelTransferOptions = parallelTransferOptions; + return this; + } + + /** + * @return {@link BlobHttpHeaders} + */ + public BlobHttpHeaders getHeaders() { + return headers; + } + + /** + * @param headers {@link BlobHttpHeaders} + * @return The updated options + */ + public BlobParallelUploadOptions setHeaders(BlobHttpHeaders headers) { + this.headers = headers; + return this; + } + + /** + * @return The metadata to associate with the blob. + */ + public Map getMetadata() { + return metadata; + } + + /** + * @param metadata The metadata to associate with the blob. + * @return The updated options. + */ + public BlobParallelUploadOptions setMetadata(Map metadata) { + this.metadata = metadata; + return this; + } + + /** + * @return {@link AccessTier} + */ + public AccessTier getTier() { + return tier; + } + + /** + * @param tier {@link AccessTier} + * @return The updated options. + */ + public BlobParallelUploadOptions setTier(AccessTier tier) { + this.tier = tier; + return this; + } + + /** + * @return {@link BlobRequestConditions} + */ + public BlobRequestConditions getRequestConditions() { + return requestConditions; + } + + /** + * @param requestConditions {@link BlobRequestConditions} + * @return The updated options. + */ + public BlobParallelUploadOptions setRequestConditions(BlobRequestConditions requestConditions) { + this.requestConditions = requestConditions; + return this; + } +} diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/APISpec.groovy b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/APISpec.groovy index 575b09769f09..8cb174b10403 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/APISpec.groovy +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/APISpec.groovy @@ -204,7 +204,6 @@ class APISpec extends Specification { interceptorManager.close() } - //TODO: Should this go in core. static Mono collectBytesInBuffer(Flux content) { return FluxUtil.collectBytesInByteBufferStream(content).map { bytes -> ByteBuffer.wrap(bytes) } } diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobAPITest.groovy b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobAPITest.groovy index c41a3779d0e7..caa9edaf7596 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobAPITest.groovy +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobAPITest.groovy @@ -101,6 +101,9 @@ class BlobAPITest extends APISpec { @Unroll def "Upload numBlocks"() { setup: + if (numBlocks > 0 && !liveMode()) { + return // skip multipart upload for playback/record as it uses randomly generated block ids + } def randomData = getRandomByteArray(size) def input = new ByteArrayInputStream(randomData) @@ -121,6 +124,11 @@ class BlobAPITest extends APISpec { 3 * Constants.MB| Constants.MB || 3 } + def "Upload return value"() { + expect: + bc.uploadWithResponse(defaultInputStream.get(), defaultDataSize, null, null, null).getValue().getETag() != null + } + @Requires({ liveMode() }) // Reading from recordings will not allow for the timing of the test to work correctly. def "Upload timeout"() { setup: diff --git a/sdk/storage/azure-storage-blob/src/test/resources/session-records/BlobAPITestuploadreturnvalue.json b/sdk/storage/azure-storage-blob/src/test/resources/session-records/BlobAPITestuploadreturnvalue.json new file mode 100644 index 000000000000..9945f95a2dfc --- /dev/null +++ b/sdk/storage/azure-storage-blob/src/test/resources/session-records/BlobAPITestuploadreturnvalue.json @@ -0,0 +1,115 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://REDACTED.blob.core.windows.net/jtcuploadreturnvalue0blobapitestuploadreturnvalueac375005572?restype=container", + "Headers" : { + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "18215bde-49e1-4684-98b4-1ffc35ae1fe5" + }, + "Response" : { + "x-ms-version" : "2019-07-07", + "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "0x8D801A7397A7BE3", + "Last-Modified" : "Tue, 26 May 2020 19:01:40 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dc92cad7-e01e-0130-2090-330d62000000", + "Date" : "Tue, 26 May 2020 19:01:40 GMT", + "x-ms-client-request-id" : "18215bde-49e1-4684-98b4-1ffc35ae1fe5" + }, + "Exception" : null + }, { + "Method" : "PUT", + "Uri" : "https://REDACTED.blob.core.windows.net/jtcuploadreturnvalue0blobapitestuploadreturnvalueac375005572/javablobuploadreturnvalue1blobapitestuploadreturnvalueac350727", + "Headers" : { + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "c32a1b3d-0aaf-4bc7-815a-eb26ea2e2d9c", + "Content-Type" : "application/octet-stream" + }, + "Response" : { + "x-ms-version" : "2019-07-07", + "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-content-crc64" : "6RYQPwaVsyQ=", + "Last-Modified" : "Tue, 26 May 2020 19:01:41 GMT", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 26 May 2020 19:01:41 GMT", + "Content-MD5" : "wh+Wm18D0z1D4E+PE252gg==", + "ETag" : "0x8D801A739B591A0", + "Content-Length" : "0", + "x-ms-request-id" : "35a22e23-e01e-0016-6c90-33d083000000", + "x-ms-client-request-id" : "c32a1b3d-0aaf-4bc7-815a-eb26ea2e2d9c" + }, + "Exception" : null + }, { + "Method" : "PUT", + "Uri" : "https://REDACTED.blob.core.windows.net/jtcuploadreturnvalue0blobapitestuploadreturnvalueac375005572/javablobuploadreturnvalue1blobapitestuploadreturnvalueac350727", + "Headers" : { + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "97217080-b607-4c5a-9377-9402de2fbbb0", + "Content-Type" : "application/octet-stream" + }, + "Response" : { + "x-ms-version" : "2019-07-07", + "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-content-crc64" : "6RYQPwaVsyQ=", + "Last-Modified" : "Tue, 26 May 2020 19:01:41 GMT", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 26 May 2020 19:01:40 GMT", + "Content-MD5" : "wh+Wm18D0z1D4E+PE252gg==", + "ETag" : "0x8D801A739DBE7A7", + "Content-Length" : "0", + "x-ms-request-id" : "45a43c8b-501e-0129-5090-33210a000000", + "x-ms-client-request-id" : "97217080-b607-4c5a-9377-9402de2fbbb0" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.blob.core.windows.net?prefix=jtcuploadreturnvalue&comp=list", + "Headers" : { + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "77ce1921-fe00-4f0d-89e6-81b20026fb4a" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2019-07-07", + "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "953dace8-b01e-0128-5690-3320f7000000", + "Body" : "jtcuploadreturnvaluejtcuploadreturnvalue0blobapitestuploadreturnvalueac375005572Tue, 26 May 2020 19:01:40 GMT\"0x8D801A7397A7BE3\"unlockedavailable$account-encryption-keyfalsefalsefalse", + "Date" : "Tue, 26 May 2020 19:01:41 GMT", + "x-ms-client-request-id" : "77ce1921-fe00-4f0d-89e6-81b20026fb4a", + "Content-Type" : "application/xml" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.blob.core.windows.net/jtcuploadreturnvalue0blobapitestuploadreturnvalueac375005572?restype=container", + "Headers" : { + "x-ms-version" : "2019-07-07", + "User-Agent" : "azsdk-java-azure-storage-blob/12.7.0-beta.1 (11.0.5; Windows 10 10.0)", + "x-ms-client-request-id" : "7bd95de9-39e6-43c6-9798-8e034a96a037" + }, + "Response" : { + "x-ms-version" : "2019-07-07", + "Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "ef6e1ddd-001e-001c-4a90-33c90a000000", + "Date" : "Tue, 26 May 2020 19:01:41 GMT", + "x-ms-client-request-id" : "7bd95de9-39e6-43c6-9798-8e034a96a037" + }, + "Exception" : null + } ], + "variables" : [ "jtcuploadreturnvalue0blobapitestuploadreturnvalueac375005572", "javablobuploadreturnvalue1blobapitestuploadreturnvalueac350727" ] +} \ No newline at end of file