diff --git a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.net6.0.cs b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.net6.0.cs
index 5ee55f5151499..984cf705c98a8 100644
--- a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.net6.0.cs
+++ b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.net6.0.cs
@@ -506,6 +506,7 @@ public BlobDownloadDetails() { }
public long BlobSequenceNumber { get { throw null; } }
public Azure.Storage.Blobs.Models.BlobType BlobType { get { throw null; } }
public string CacheControl { get { throw null; } }
+ public byte[] ContentCrc { get { throw null; } }
public string ContentDisposition { get { throw null; } }
public string ContentEncoding { get { throw null; } }
public byte[] ContentHash { get { throw null; } }
diff --git a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs
index 5ee55f5151499..984cf705c98a8 100644
--- a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs
+++ b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs
@@ -506,6 +506,7 @@ public BlobDownloadDetails() { }
public long BlobSequenceNumber { get { throw null; } }
public Azure.Storage.Blobs.Models.BlobType BlobType { get { throw null; } }
public string CacheControl { get { throw null; } }
+ public byte[] ContentCrc { get { throw null; } }
public string ContentDisposition { get { throw null; } }
public string ContentEncoding { get { throw null; } }
public byte[] ContentHash { get { throw null; } }
diff --git a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.1.cs b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.1.cs
index 5ee55f5151499..984cf705c98a8 100644
--- a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.1.cs
+++ b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.1.cs
@@ -506,6 +506,7 @@ public BlobDownloadDetails() { }
public long BlobSequenceNumber { get { throw null; } }
public Azure.Storage.Blobs.Models.BlobType BlobType { get { throw null; } }
public string CacheControl { get { throw null; } }
+ public byte[] ContentCrc { get { throw null; } }
public string ContentDisposition { get { throw null; } }
public string ContentEncoding { get { throw null; } }
public byte[] ContentHash { get { throw null; } }
diff --git a/sdk/storage/Azure.Storage.Blobs/assets.json b/sdk/storage/Azure.Storage.Blobs/assets.json
index 45749c782345c..80a059129311a 100644
--- a/sdk/storage/Azure.Storage.Blobs/assets.json
+++ b/sdk/storage/Azure.Storage.Blobs/assets.json
@@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "net",
"TagPrefix": "net/storage/Azure.Storage.Blobs",
- "Tag": "net/storage/Azure.Storage.Blobs_6a63d70288"
+ "Tag": "net/storage/Azure.Storage.Blobs_90f12645a2"
}
diff --git a/sdk/storage/Azure.Storage.Blobs/src/Azure.Storage.Blobs.csproj b/sdk/storage/Azure.Storage.Blobs/src/Azure.Storage.Blobs.csproj
index ac8917e1e268d..8f9b13c6482cd 100644
--- a/sdk/storage/Azure.Storage.Blobs/src/Azure.Storage.Blobs.csproj
+++ b/sdk/storage/Azure.Storage.Blobs/src/Azure.Storage.Blobs.csproj
@@ -50,6 +50,7 @@
+
diff --git a/sdk/storage/Azure.Storage.Blobs/src/BlobBaseClient.cs b/sdk/storage/Azure.Storage.Blobs/src/BlobBaseClient.cs
index 7299b5e07b7f6..9bc42bba73f2a 100644
--- a/sdk/storage/Azure.Storage.Blobs/src/BlobBaseClient.cs
+++ b/sdk/storage/Azure.Storage.Blobs/src/BlobBaseClient.cs
@@ -1571,7 +1571,11 @@ ValueTask> Factory(long offset, bool force
.EnsureCompleted(),
async startOffset => await StructuredMessageFactory(startOffset, async: true, cancellationToken)
.ConfigureAwait(false),
- default, //decodedData => response.Value.Details.ContentCrc = decodedData.TotalCrc.ToArray(),
+ decodedData =>
+ {
+ response.Value.Details.ContentCrc = new byte[StructuredMessage.Crc64Length];
+ decodedData.Crc.WriteCrc64(response.Value.Details.ContentCrc);
+ },
ClientConfiguration.Pipeline.ResponseClassifier,
Constants.MaxReliabilityRetries);
}
diff --git a/sdk/storage/Azure.Storage.Blobs/src/BlobClientOptions.cs b/sdk/storage/Azure.Storage.Blobs/src/BlobClientOptions.cs
index c4ca671302801..97f67c63450ea 100644
--- a/sdk/storage/Azure.Storage.Blobs/src/BlobClientOptions.cs
+++ b/sdk/storage/Azure.Storage.Blobs/src/BlobClientOptions.cs
@@ -317,6 +317,8 @@ private void AddHeadersAndQueryParameters()
Diagnostics.LoggedHeaderNames.Add("x-ms-source-if-unmodified-since");
Diagnostics.LoggedHeaderNames.Add("x-ms-tag-count");
Diagnostics.LoggedHeaderNames.Add("x-ms-encryption-key-sha256");
+ Diagnostics.LoggedHeaderNames.Add("x-ms-structured-body");
+ Diagnostics.LoggedHeaderNames.Add("x-ms-structured-content-length");
Diagnostics.LoggedQueryParameters.Add("comp");
Diagnostics.LoggedQueryParameters.Add("maxresults");
diff --git a/sdk/storage/Azure.Storage.Blobs/src/Models/BlobDownloadDetails.cs b/sdk/storage/Azure.Storage.Blobs/src/Models/BlobDownloadDetails.cs
index 6104abfd9ac5f..0490ec239798e 100644
--- a/sdk/storage/Azure.Storage.Blobs/src/Models/BlobDownloadDetails.cs
+++ b/sdk/storage/Azure.Storage.Blobs/src/Models/BlobDownloadDetails.cs
@@ -34,14 +34,13 @@ public class BlobDownloadDetails
public byte[] ContentHash { get; internal set; }
#pragma warning restore CA1819 // Properties should not return arrays
- // TODO enable in following PR
- /////
- ///// When requested using , this value contains the CRC for the download blob range.
- ///// This value may only become populated once the network stream is fully consumed. If this instance is accessed through
- ///// , the network stream has already been consumed. Otherwise, consume the content stream before
- ///// checking this value.
- /////
- //public byte[] ContentCrc { get; internal set; }
+ ///
+ /// When requested using , this value contains the CRC for the download blob range.
+ /// This value may only become populated once the network stream is fully consumed. If this instance is accessed through
+ /// , the network stream has already been consumed. Otherwise, consume the content stream before
+ /// checking this value.
+ ///
+ public byte[] ContentCrc { get; internal set; }
///
/// Returns the date and time the container was last modified. Any operation that modifies the blob, including an update of the blob's metadata or properties, changes the last-modified time of the blob.
diff --git a/sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTransferValidationTests.cs b/sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTransferValidationTests.cs
index 76d807835873c..c502231087ed6 100644
--- a/sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTransferValidationTests.cs
+++ b/sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTransferValidationTests.cs
@@ -124,6 +124,68 @@ public virtual async Task OlderServiceVersionThrowsOnStructuredMessage()
})).Value.Content.CopyToAsync(Stream.Null);
Assert.That(operation, Throws.TypeOf());
}
+
+ [Test]
+ public async Task StructuredMessagePopulatesCrcDownloadStreaming()
+ {
+ await using DisposingContainer disposingContainer = await ClientBuilder.GetTestContainerAsync(
+ publicAccessType: PublicAccessType.None);
+
+ const int dataLength = Constants.KB;
+ byte[] data = GetRandomBuffer(dataLength);
+ byte[] dataCrc = new byte[8];
+ StorageCrc64Calculator.ComputeSlicedSafe(data, 0L).WriteCrc64(dataCrc);
+
+ var blob = disposingContainer.Container.GetBlobClient(GetNewResourceName());
+ await blob.UploadAsync(BinaryData.FromBytes(data));
+
+ Response response = await blob.DownloadStreamingAsync(new()
+ {
+ TransferValidation = new DownloadTransferValidationOptions
+ {
+ ChecksumAlgorithm = StorageChecksumAlgorithm.StorageCrc64
+ }
+ });
+
+ // crc is not present until response stream is consumed
+ Assert.That(response.Value.Details.ContentCrc, Is.Null);
+
+ byte[] downloadedData;
+ using (MemoryStream ms = new())
+ {
+ await response.Value.Content.CopyToAsync(ms);
+ downloadedData = ms.ToArray();
+ }
+
+ Assert.That(response.Value.Details.ContentCrc, Is.EqualTo(dataCrc));
+ Assert.That(downloadedData, Is.EqualTo(data));
+ }
+
+ [Test]
+ public async Task StructuredMessagePopulatesCrcDownloadContent()
+ {
+ await using DisposingContainer disposingContainer = await ClientBuilder.GetTestContainerAsync(
+ publicAccessType: PublicAccessType.None);
+
+ const int dataLength = Constants.KB;
+ byte[] data = GetRandomBuffer(dataLength);
+ byte[] dataCrc = new byte[8];
+ StorageCrc64Calculator.ComputeSlicedSafe(data, 0L).WriteCrc64(dataCrc);
+
+ var blob = disposingContainer.Container.GetBlobClient(GetNewResourceName());
+ await blob.UploadAsync(BinaryData.FromBytes(data));
+
+ Response response = await blob.DownloadContentAsync(new BlobDownloadOptions()
+ {
+ TransferValidation = new DownloadTransferValidationOptions
+ {
+ ChecksumAlgorithm = StorageChecksumAlgorithm.StorageCrc64
+ }
+ });
+
+ Assert.That(response.Value.Details.ContentCrc, Is.EqualTo(dataCrc));
+ Assert.That(response.Value.Content.ToArray(), Is.EqualTo(data));
+ }
#endregion
}
}
diff --git a/sdk/storage/Azure.Storage.Common/src/Shared/ChecksumExtensions.cs b/sdk/storage/Azure.Storage.Common/src/Shared/ChecksumExtensions.cs
new file mode 100644
index 0000000000000..48304640eee43
--- /dev/null
+++ b/sdk/storage/Azure.Storage.Common/src/Shared/ChecksumExtensions.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Buffers.Binary;
+
+namespace Azure.Storage;
+
+internal static class ChecksumExtensions
+{
+ public static void WriteCrc64(this ulong crc, Span dest)
+ => BinaryPrimitives.WriteUInt64LittleEndian(dest, crc);
+
+ public static bool TryWriteCrc64(this ulong crc, Span dest)
+ => BinaryPrimitives.TryWriteUInt64LittleEndian(dest, crc);
+
+ public static ulong ReadCrc64(this ReadOnlySpan crc)
+ => BinaryPrimitives.ReadUInt64LittleEndian(crc);
+
+ public static bool TryReadCrc64(this ReadOnlySpan crc, out ulong value)
+ => BinaryPrimitives.TryReadUInt64LittleEndian(crc, out value);
+}
diff --git a/sdk/storage/Azure.Storage.Common/src/Shared/StructuredMessage.cs b/sdk/storage/Azure.Storage.Common/src/Shared/StructuredMessage.cs
index 89d3b0df05bfc..92c4cd0f07405 100644
--- a/sdk/storage/Azure.Storage.Common/src/Shared/StructuredMessage.cs
+++ b/sdk/storage/Azure.Storage.Common/src/Shared/StructuredMessage.cs
@@ -104,7 +104,7 @@ public static void ReadStreamFooter(
int expectedBufferSize = GetSegmentFooterSize(flags);
Errors.AssertBufferExactSize(buffer, expectedBufferSize, nameof(buffer));
- crc64 = flags.HasFlag(Flags.StorageCrc64) ? BinaryPrimitives.ReadUInt64LittleEndian(buffer) : default;
+ crc64 = flags.HasFlag(Flags.StorageCrc64) ? buffer.ReadCrc64() : default;
}
public static int WriteStreamFooter(Span buffer, ReadOnlySpan crc64 = default)
@@ -200,7 +200,7 @@ public static void ReadSegmentFooter(
int expectedBufferSize = GetSegmentFooterSize(flags);
Errors.AssertBufferExactSize(buffer, expectedBufferSize, nameof(buffer));
- crc64 = flags.HasFlag(Flags.StorageCrc64) ? BinaryPrimitives.ReadUInt64LittleEndian(buffer) : default;
+ crc64 = flags.HasFlag(Flags.StorageCrc64) ? buffer.ReadCrc64() : default;
}
public static int WriteSegmentFooter(Span buffer, ReadOnlySpan crc64 = default)
diff --git a/sdk/storage/Azure.Storage.Common/src/Shared/StructuredMessageDecodingStream.cs b/sdk/storage/Azure.Storage.Common/src/Shared/StructuredMessageDecodingStream.cs
index 439fcab0e80b8..f3d2a3049c512 100644
--- a/sdk/storage/Azure.Storage.Common/src/Shared/StructuredMessageDecodingStream.cs
+++ b/sdk/storage/Azure.Storage.Common/src/Shared/StructuredMessageDecodingStream.cs
@@ -431,20 +431,11 @@ private int ProcessStreamFooter(ReadOnlySpan span)
out ulong reportedCrc);
if (_decodedData.Flags.Value.HasFlag(StructuredMessage.Flags.StorageCrc64))
{
- _decodedData.TotalCrc = reportedCrc;
if (_validateChecksums)
{
- using (ArrayPool.Shared.RentDisposable(StructuredMessage.Crc64Length * 2, out byte[] buf))
- {
- Span calculated = new(buf, 0, StructuredMessage.Crc64Length);
- _totalContentCrc.GetCurrentHash(calculated);
- if (BinaryPrimitives.ReadUInt64LittleEndian(calculated) != reportedCrc)
- {
- Span reportedAsBytes = new(buf, calculated.Length, StructuredMessage.Crc64Length);
- throw Errors.ChecksumMismatch(calculated, reportedAsBytes);
- }
- }
+ ValidateCrc64(_totalContentCrc, reportedCrc);
}
+ _decodedData.TotalCrc = reportedCrc;
}
if (_innerStreamConsumed != _decodedData.InnerStreamLength)
@@ -487,23 +478,27 @@ private int ProcessSegmentFooter(ReadOnlySpan span)
{
if (_validateChecksums)
{
- using (ArrayPool.Shared.RentDisposable(StructuredMessage.Crc64Length * 2, out byte[] buf))
- {
- Span calculated = new(buf, 0, StructuredMessage.Crc64Length);
- _segmentCrc.GetCurrentHash(calculated);
- _segmentCrc = StorageCrc64HashAlgorithm.Create();
- if (BinaryPrimitives.ReadUInt64LittleEndian(calculated) != reportedCrc)
- {
- Span reportedAsBytes = new(buf, calculated.Length, StructuredMessage.Crc64Length);
- throw Errors.ChecksumMismatch(calculated, reportedAsBytes);
- }
- }
+ ValidateCrc64(_segmentCrc, reportedCrc);
+ _segmentCrc = StorageCrc64HashAlgorithm.Create();
}
_decodedData.SegmentCrcs.Add((reportedCrc, _currentSegmentContentLength));
}
_currentRegion = _currentSegmentNum == _decodedData.TotalSegments ? SMRegion.StreamFooter : SMRegion.SegmentHeader;
return footerLen;
}
+
+ private static void ValidateCrc64(StorageCrc64HashAlgorithm calculation, ulong reported)
+ {
+ using IDisposable _ = ArrayPool.Shared.RentDisposable(StructuredMessage.Crc64Length * 2, out byte[] buf);
+ Span calculatedBytes = new(buf, 0, StructuredMessage.Crc64Length);
+ Span reportedBytes = new(buf, calculatedBytes.Length, StructuredMessage.Crc64Length);
+ calculation.GetCurrentHash(calculatedBytes);
+ reported.WriteCrc64(reportedBytes);
+ if (!calculatedBytes.SequenceEqual(reportedBytes))
+ {
+ throw Errors.ChecksumMismatch(calculatedBytes, reportedBytes);
+ }
+ }
#endregion
public override long Seek(long offset, SeekOrigin origin)
diff --git a/sdk/storage/Azure.Storage.Common/tests/Azure.Storage.Common.Tests.csproj b/sdk/storage/Azure.Storage.Common/tests/Azure.Storage.Common.Tests.csproj
index 32c114d1b718c..10a4b50e0a272 100644
--- a/sdk/storage/Azure.Storage.Common/tests/Azure.Storage.Common.Tests.csproj
+++ b/sdk/storage/Azure.Storage.Common/tests/Azure.Storage.Common.Tests.csproj
@@ -31,6 +31,7 @@
+
diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/Azure.Storage.Files.DataLake.csproj b/sdk/storage/Azure.Storage.Files.DataLake/src/Azure.Storage.Files.DataLake.csproj
index bbe44751e1e1e..8b233a70d69a8 100644
--- a/sdk/storage/Azure.Storage.Files.DataLake/src/Azure.Storage.Files.DataLake.csproj
+++ b/sdk/storage/Azure.Storage.Files.DataLake/src/Azure.Storage.Files.DataLake.csproj
@@ -41,6 +41,7 @@
+
diff --git a/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.net6.0.cs b/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.net6.0.cs
index d227747235ba8..04a6db8fb786e 100644
--- a/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.net6.0.cs
+++ b/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.net6.0.cs
@@ -715,6 +715,7 @@ public partial class ShareFileDownloadInfo : System.IDisposable
{
internal ShareFileDownloadInfo() { }
public System.IO.Stream Content { get { throw null; } }
+ public byte[] ContentCrc { get { throw null; } }
public byte[] ContentHash { get { throw null; } }
public long ContentLength { get { throw null; } }
public string ContentType { get { throw null; } }
diff --git a/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.netstandard2.0.cs b/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.netstandard2.0.cs
index d227747235ba8..04a6db8fb786e 100644
--- a/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.netstandard2.0.cs
+++ b/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.netstandard2.0.cs
@@ -715,6 +715,7 @@ public partial class ShareFileDownloadInfo : System.IDisposable
{
internal ShareFileDownloadInfo() { }
public System.IO.Stream Content { get { throw null; } }
+ public byte[] ContentCrc { get { throw null; } }
public byte[] ContentHash { get { throw null; } }
public long ContentLength { get { throw null; } }
public string ContentType { get { throw null; } }
diff --git a/sdk/storage/Azure.Storage.Files.Shares/assets.json b/sdk/storage/Azure.Storage.Files.Shares/assets.json
index 3267ba7705f67..5ab7ab3d2a75d 100644
--- a/sdk/storage/Azure.Storage.Files.Shares/assets.json
+++ b/sdk/storage/Azure.Storage.Files.Shares/assets.json
@@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "net",
"TagPrefix": "net/storage/Azure.Storage.Files.Shares",
- "Tag": "net/storage/Azure.Storage.Files.Shares_e12d252b83"
+ "Tag": "net/storage/Azure.Storage.Files.Shares_aca4166ca6"
}
diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Azure.Storage.Files.Shares.csproj b/sdk/storage/Azure.Storage.Files.Shares/src/Azure.Storage.Files.Shares.csproj
index 304221b605abe..6360dbb3081b8 100644
--- a/sdk/storage/Azure.Storage.Files.Shares/src/Azure.Storage.Files.Shares.csproj
+++ b/sdk/storage/Azure.Storage.Files.Shares/src/Azure.Storage.Files.Shares.csproj
@@ -1,4 +1,4 @@
-
+
$(RequiredTargetFrameworks);net6.0
@@ -41,6 +41,7 @@
+
diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareFileDownloadInfo.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareFileDownloadInfo.cs
index bf8aff61a72a0..2d3810a6714cd 100644
--- a/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareFileDownloadInfo.cs
+++ b/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareFileDownloadInfo.cs
@@ -38,6 +38,12 @@ public partial class ShareFileDownloadInfo : IDisposable, IDownloadedContent
public byte[] ContentHash { get; internal set; }
#pragma warning restore CA1819 // Properties should not return arrays
+ ///
+ /// When requested using , this value contains the CRC for the download blob range.
+ /// This value may only become populated once the network stream is fully consumed.
+ ///
+ public byte[] ContentCrc { get; internal set; }
+
///
/// Details returned when downloading a file
///
diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/ShareFileClient.cs b/sdk/storage/Azure.Storage.Files.Shares/src/ShareFileClient.cs
index 60d3eebe2ab7b..7f25102b709b8 100644
--- a/sdk/storage/Azure.Storage.Files.Shares/src/ShareFileClient.cs
+++ b/sdk/storage/Azure.Storage.Files.Shares/src/ShareFileClient.cs
@@ -2306,7 +2306,11 @@ async ValueTask> Factory(long offset, bool async
.EnsureCompleted(),
async startOffset => await StructuredMessageFactory(startOffset, async: true, cancellationToken)
.ConfigureAwait(false),
- default, //decodedData => response.Value.Details.ContentCrc = decodedData.TotalCrc.ToArray(),
+ decodedData =>
+ {
+ initialResponse.Value.ContentCrc = new byte[StructuredMessage.Crc64Length];
+ decodedData.Crc.WriteCrc64(initialResponse.Value.ContentCrc);
+ },
ClientConfiguration.Pipeline.ResponseClassifier,
Constants.MaxReliabilityRetries);
}
diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/ShareFileClientTransferValidationTests.cs b/sdk/storage/Azure.Storage.Files.Shares/tests/ShareFileClientTransferValidationTests.cs
index afe33c95847d0..4cfa2b7271065 100644
--- a/sdk/storage/Azure.Storage.Files.Shares/tests/ShareFileClientTransferValidationTests.cs
+++ b/sdk/storage/Azure.Storage.Files.Shares/tests/ShareFileClientTransferValidationTests.cs
@@ -146,5 +146,41 @@ public override void TestAutoResolve()
StorageChecksumAlgorithm.MD5,
TransferValidationOptionsExtensions.ResolveAuto(StorageChecksumAlgorithm.Auto));
}
+
+ [Test]
+ public async Task StructuredMessagePopulatesCrcDownloadStreaming()
+ {
+ await using DisposingShare disposingContainer = await ClientBuilder.GetTestShareAsync();
+
+ const int dataLength = Constants.KB;
+ byte[] data = GetRandomBuffer(dataLength);
+ byte[] dataCrc = new byte[8];
+ StorageCrc64Calculator.ComputeSlicedSafe(data, 0L).WriteCrc64(dataCrc);
+
+ ShareFileClient file = disposingContainer.Container.GetRootDirectoryClient().GetFileClient(GetNewResourceName());
+ await file.CreateAsync(data.Length);
+ await file.UploadAsync(new MemoryStream(data));
+
+ Response response = await file.DownloadAsync(new ShareFileDownloadOptions()
+ {
+ TransferValidation = new DownloadTransferValidationOptions
+ {
+ ChecksumAlgorithm = StorageChecksumAlgorithm.StorageCrc64
+ }
+ });
+
+ // crc is not present until response stream is consumed
+ Assert.That(response.Value.ContentCrc, Is.Null);
+
+ byte[] downloadedData;
+ using (MemoryStream ms = new())
+ {
+ await response.Value.Content.CopyToAsync(ms);
+ downloadedData = ms.ToArray();
+ }
+
+ Assert.That(response.Value.ContentCrc, Is.EqualTo(dataCrc));
+ Assert.That(downloadedData, Is.EqualTo(data));
+ }
}
}