Skip to content

Commit 1f6a04d

Browse files
Complete integration of structured message decoder with all blob download methods
Co-authored-by: gunjansingh-msft <[email protected]>
1 parent ceedf55 commit 1f6a04d

File tree

5 files changed

+134
-4
lines changed

5 files changed

+134
-4
lines changed

sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/options/BlobInputStreamOptions.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.azure.storage.blob.models.BlobRange;
88
import com.azure.storage.blob.models.BlobRequestConditions;
99
import com.azure.storage.blob.models.ConsistentReadControl;
10+
import com.azure.storage.common.implementation.contentvalidation.DownloadContentValidationOptions;
1011

1112
/**
1213
* Extended options that may be passed when opening a blob input stream.
@@ -17,6 +18,7 @@ public class BlobInputStreamOptions {
1718
private BlobRequestConditions requestConditions;
1819
private Integer blockSize;
1920
private ConsistentReadControl consistentReadControl;
21+
private DownloadContentValidationOptions contentValidationOptions;
2022

2123
/**
2224
* Creates a new instance of {@link BlobInputStreamOptions}.
@@ -111,4 +113,24 @@ public BlobInputStreamOptions setConsistentReadControl(ConsistentReadControl con
111113
this.consistentReadControl = consistentReadControl;
112114
return this;
113115
}
116+
117+
/**
118+
* Gets the {@link DownloadContentValidationOptions}.
119+
*
120+
* @return {@link DownloadContentValidationOptions}
121+
*/
122+
public DownloadContentValidationOptions getContentValidationOptions() {
123+
return contentValidationOptions;
124+
}
125+
126+
/**
127+
* Sets the {@link DownloadContentValidationOptions}.
128+
*
129+
* @param contentValidationOptions {@link DownloadContentValidationOptions}
130+
* @return The updated options.
131+
*/
132+
public BlobInputStreamOptions setContentValidationOptions(DownloadContentValidationOptions contentValidationOptions) {
133+
this.contentValidationOptions = contentValidationOptions;
134+
return this;
135+
}
114136
}

sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/options/BlobSeekableByteChannelReadOptions.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.azure.core.annotation.Fluent;
77
import com.azure.storage.blob.models.BlobRequestConditions;
88
import com.azure.storage.blob.models.ConsistentReadControl;
9+
import com.azure.storage.common.implementation.contentvalidation.DownloadContentValidationOptions;
910

1011
import java.nio.channels.SeekableByteChannel;
1112

@@ -18,6 +19,7 @@ public final class BlobSeekableByteChannelReadOptions {
1819
private BlobRequestConditions requestConditions;
1920
private Integer readSizeInBytes;
2021
private ConsistentReadControl consistentReadControl;
22+
private DownloadContentValidationOptions contentValidationOptions;
2123

2224
/**
2325
* Creates a new instance of {@link BlobSeekableByteChannelReadOptions}.
@@ -108,4 +110,24 @@ public BlobSeekableByteChannelReadOptions setConsistentReadControl(ConsistentRea
108110
this.consistentReadControl = consistentReadControl;
109111
return this;
110112
}
113+
114+
/**
115+
* Gets the {@link DownloadContentValidationOptions}.
116+
*
117+
* @return {@link DownloadContentValidationOptions}
118+
*/
119+
public DownloadContentValidationOptions getContentValidationOptions() {
120+
return contentValidationOptions;
121+
}
122+
123+
/**
124+
* Sets the {@link DownloadContentValidationOptions}.
125+
*
126+
* @param contentValidationOptions {@link DownloadContentValidationOptions}
127+
* @return The updated options.
128+
*/
129+
public BlobSeekableByteChannelReadOptions setContentValidationOptions(DownloadContentValidationOptions contentValidationOptions) {
130+
this.contentValidationOptions = contentValidationOptions;
131+
return this;
132+
}
111133
}

sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/specialized/BlobAsyncClientBase.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,6 +1263,57 @@ public Mono<BlobDownloadContentAsyncResponse> downloadContentWithResponse(Downlo
12631263
}
12641264
}
12651265

1266+
/**
1267+
* Reads the entire blob with content validation options. Uploading data must be done from the {@link BlockBlobClient}, {@link
1268+
* PageBlobClient}, or {@link AppendBlobClient}.
1269+
*
1270+
* <p><strong>Code Samples</strong></p>
1271+
*
1272+
* <pre>{@code
1273+
* DownloadRetryOptions options = new DownloadRetryOptions().setMaxRetryRequests(5);
1274+
* DownloadContentValidationOptions validationOptions = new DownloadContentValidationOptions()
1275+
* .setStructuredMessageValidationEnabled(true);
1276+
*
1277+
* client.downloadContentWithResponse(options, null, validationOptions).subscribe(response -> {
1278+
* BinaryData content = response.getValue();
1279+
* System.out.println(content.toString());
1280+
* });
1281+
* }</pre>
1282+
*
1283+
* <p>For more information, see the
1284+
* <a href="https://docs.microsoft.com/rest/api/storageservices/get-blob">Azure Docs</a></p>
1285+
*
1286+
* <p>This method supports downloads up to 2GB of data. Content will be buffered in memory. If the blob is larger,
1287+
* use {@link #downloadStreamWithResponse(BlobRange, DownloadRetryOptions, BlobRequestConditions, boolean, DownloadContentValidationOptions)}
1288+
* to download larger blobs.</p>
1289+
*
1290+
* @param options {@link DownloadRetryOptions}
1291+
* @param requestConditions {@link BlobRequestConditions}
1292+
* @param contentValidationOptions {@link DownloadContentValidationOptions} options for content validation
1293+
* @return A reactive response containing the blob data.
1294+
*/
1295+
@ServiceMethod(returns = ReturnType.SINGLE)
1296+
public Mono<BlobDownloadContentAsyncResponse> downloadContentWithResponse(DownloadRetryOptions options,
1297+
BlobRequestConditions requestConditions, DownloadContentValidationOptions contentValidationOptions) {
1298+
try {
1299+
return withContext(context -> {
1300+
if (contentValidationOptions != null && (contentValidationOptions.isStructuredMessageValidationEnabled() || contentValidationOptions.isMd5ValidationEnabled())) {
1301+
return downloadStreamWithResponse(null, options, requestConditions, false, contentValidationOptions, context)
1302+
.flatMap(r -> BinaryData.fromFlux(r.getValue())
1303+
.map(data -> new BlobDownloadContentAsyncResponse(r.getRequest(), r.getStatusCode(), r.getHeaders(),
1304+
data, r.getDeserializedHeaders())));
1305+
} else {
1306+
return downloadStreamWithResponse(null, options, requestConditions, false, context)
1307+
.flatMap(r -> BinaryData.fromFlux(r.getValue())
1308+
.map(data -> new BlobDownloadContentAsyncResponse(r.getRequest(), r.getStatusCode(), r.getHeaders(),
1309+
data, r.getDeserializedHeaders())));
1310+
}
1311+
});
1312+
} catch (RuntimeException ex) {
1313+
return monoError(LOGGER, ex);
1314+
}
1315+
}
1316+
12661317
Mono<BlobDownloadAsyncResponse> downloadStreamWithResponse(BlobRange range, DownloadRetryOptions options,
12671318
BlobRequestConditions requestConditions, boolean getRangeContentMd5, Context context) {
12681319
BlobRange finalRange = range == null ? new BlobRange(0) : range;

sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/specialized/BlobClientBase.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
import com.azure.storage.common.implementation.SasImplUtils;
9292
import com.azure.storage.common.implementation.StorageImplUtils;
9393
import com.azure.storage.common.implementation.StorageSeekableByteChannel;
94+
import com.azure.storage.common.implementation.contentvalidation.DownloadContentValidationOptions;
9495
import reactor.core.publisher.Mono;
9596

9697
import java.io.IOException;
@@ -511,7 +512,14 @@ public BlobInputStream openInputStream(BlobInputStreamOptions options, Context c
511512
com.azure.storage.common.ParallelTransferOptions parallelTransferOptions
512513
= new com.azure.storage.common.ParallelTransferOptions().setBlockSizeLong((long) chunkSize);
513514
BiFunction<BlobRange, BlobRequestConditions, Mono<BlobDownloadAsyncResponse>> downloadFunc = (chunkRange,
514-
conditions) -> client.downloadStreamWithResponse(chunkRange, null, conditions, false, contextFinal);
515+
conditions) -> {
516+
DownloadContentValidationOptions contentValidationOptions = options.getContentValidationOptions();
517+
if (contentValidationOptions != null && (contentValidationOptions.isStructuredMessageValidationEnabled() || contentValidationOptions.isMd5ValidationEnabled())) {
518+
return client.downloadStreamWithResponse(chunkRange, null, conditions, false, contentValidationOptions, contextFinal);
519+
} else {
520+
return client.downloadStreamWithResponse(chunkRange, null, conditions, false, contextFinal);
521+
}
522+
};
515523
return ChunkedDownloadUtils
516524
.downloadFirstChunk(range, parallelTransferOptions, requestConditions, downloadFunc, true)
517525
.flatMap(tuple3 -> {
@@ -586,9 +594,22 @@ public BlobSeekableByteChannelReadResult openSeekableByteChannelRead(BlobSeekabl
586594
BlobProperties properties;
587595
BlobDownloadResponse response;
588596
try (ByteBufferBackedOutputStreamUtil dstStream = new ByteBufferBackedOutputStreamUtil(initialRange)) {
589-
response = this.downloadStreamWithResponse(dstStream,
590-
new BlobRange(initialPosition, (long) initialRange.remaining()), null /*downloadRetryOptions*/,
591-
options.getRequestConditions(), false, null, context);
597+
DownloadContentValidationOptions contentValidationOptions = options.getContentValidationOptions();
598+
if (contentValidationOptions != null && (contentValidationOptions.isStructuredMessageValidationEnabled() || contentValidationOptions.isMd5ValidationEnabled())) {
599+
BlobDownloadAsyncResponse asyncResponse = this.client.downloadStreamWithResponse(
600+
new BlobRange(initialPosition, (long) initialRange.remaining()), null /*downloadRetryOptions*/,
601+
options.getRequestConditions(), false, contentValidationOptions, context).block();
602+
if (asyncResponse != null) {
603+
// Convert async response to sync response with structured message decoding applied
604+
response = new BlobDownloadResponse(asyncResponse);
605+
} else {
606+
throw LOGGER.logExceptionAsError(new RuntimeException("Download response was null"));
607+
}
608+
} else {
609+
response = this.downloadStreamWithResponse(dstStream,
610+
new BlobRange(initialPosition, (long) initialRange.remaining()), null /*downloadRetryOptions*/,
611+
options.getRequestConditions(), false, null, context);
612+
}
592613
properties = ModelHelper.buildBlobPropertiesResponse(response).getValue();
593614
} catch (IOException e) {
594615
throw LOGGER.logExceptionAsError(new UncheckedIOException(e));

sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobAsyncClientBaseStructuredMessageTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.azure.storage.blob.models.BlobRequestConditions;
99
import com.azure.storage.blob.models.DownloadRetryOptions;
1010
import com.azure.storage.blob.options.BlobDownloadToFileOptions;
11+
import com.azure.storage.blob.options.BlobInputStreamOptions;
1112
import com.azure.storage.common.implementation.contentvalidation.DownloadContentValidationOptions;
1213
import com.azure.storage.common.implementation.structuredmessage.StructuredMessageDecoder;
1314
import com.azure.storage.common.implementation.structuredmessage.StructuredMessageEncoder;
@@ -105,6 +106,19 @@ public void testStructuredMessageEncoderDecoderIntegration() throws IOException
105106
assertArrayEquals(originalData, decodedBytes);
106107
}
107108

109+
@Test
110+
public void testBlobInputStreamOptionsWithContentValidation() {
111+
// Test setting content validation options on BlobInputStreamOptions
112+
DownloadContentValidationOptions contentValidationOptions = new DownloadContentValidationOptions()
113+
.setStructuredMessageValidationEnabled(true);
114+
115+
BlobInputStreamOptions options = new BlobInputStreamOptions()
116+
.setContentValidationOptions(contentValidationOptions);
117+
118+
assertNotNull(options.getContentValidationOptions());
119+
assertTrue(options.getContentValidationOptions().isStructuredMessageValidationEnabled());
120+
}
121+
108122
@Test
109123
public void testStructuredMessageValidationWithCrc64() throws IOException {
110124
// Test that CRC64 validation works

0 commit comments

Comments
 (0)