Skip to content

Commit ebdca3c

Browse files
authored
Seekable stream for cloud blobs (#6719)
* Added OpenReadStreamAsync to ISimpleCloudBlob to enable seekable reading from the blob storage. * null check
1 parent ff93be2 commit ebdca3c

2 files changed

Lines changed: 52 additions & 0 deletions

File tree

src/NuGetGallery.Core/Services/CloudBlobWrapper.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.IO;
77
using System.Net;
8+
using System.Threading;
89
using System.Threading.Tasks;
910
using Microsoft.WindowsAzure.Storage;
1011
using Microsoft.WindowsAzure.Storage.Blob;
@@ -29,6 +30,22 @@ public CloudBlobWrapper(CloudBlockBlob blob)
2930
_blob = blob;
3031
}
3132

33+
public static CloudBlobWrapper FromUri(Uri uri)
34+
{
35+
if (uri == null)
36+
{
37+
throw new ArgumentNullException(nameof(uri));
38+
}
39+
40+
if (!IsBlobStorageUri(uri))
41+
{
42+
throw new ArgumentException($"{nameof(uri)} must point to blob storage", nameof(uri));
43+
}
44+
45+
var blob = new CloudBlockBlob(uri);
46+
return new CloudBlobWrapper(blob);
47+
}
48+
3249
public async Task<Stream> OpenReadAsync(AccessCondition accessCondition)
3350
{
3451
return await _blob.OpenReadAsync(
@@ -141,6 +158,28 @@ await _blob.StartCopyAsync(
141158
operationContext: null);
142159
}
143160

161+
public async Task<Stream> OpenReadStreamAsync(
162+
TimeSpan serverTimeout,
163+
TimeSpan maxExecutionTime,
164+
CancellationToken cancellationToken)
165+
{
166+
var accessCondition = AccessCondition.GenerateEmptyCondition();
167+
var blobRequestOptions = new BlobRequestOptions
168+
{
169+
ServerTimeout = serverTimeout,
170+
MaximumExecutionTime = maxExecutionTime,
171+
RetryPolicy = new ExponentialRetry(),
172+
};
173+
var operationContext = new OperationContext();
174+
175+
return await _blob.OpenReadAsync(accessCondition, blobRequestOptions, operationContext, cancellationToken);
176+
}
177+
178+
private static bool IsBlobStorageUri(Uri uri)
179+
{
180+
return uri.Authority.EndsWith(".blob.core.windows.net");
181+
}
182+
144183
// The default retry policy treats a 304 as an error that requires a retry. We don't want that!
145184
private class DontRetryOnNotModifiedPolicy : IRetryPolicy
146185
{

src/NuGetGallery.Core/Services/ISimpleCloudBlob.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.IO;
7+
using System.Threading;
78
using System.Threading.Tasks;
89
using Microsoft.WindowsAzure.Storage;
910
using Microsoft.WindowsAzure.Storage.Blob;
@@ -50,5 +51,17 @@ public interface ISimpleCloudBlob
5051
/// </param>
5152
/// <returns>Shared access signature in form of URI query portion.</returns>
5253
string GetSharedAccessSignature(SharedAccessBlobPermissions permissions, DateTimeOffset? endOfAccess);
54+
55+
/// <summary>
56+
/// Opens the seekable read stream to the file in blob storage.
57+
/// </summary>
58+
/// <param name="serverTimeout">Timeout for a single HTTP request issued by implementation. See <see cref="BlobRequestOptions.ServerTimeout"/>.</param>
59+
/// <param name="maxExecutionTime">Total timeout accross all potential retries. See <see cref="BlobRequestOptions.MaximumExecutionTime"/>.</param>
60+
/// <param name="cancellationToken">Cancellation token.</param>
61+
/// <returns>Read stream for a blob in blob storage.</returns>
62+
Task<Stream> OpenReadStreamAsync(
63+
TimeSpan serverTimeout,
64+
TimeSpan maxExecutionTime,
65+
CancellationToken cancellationToken);
5366
}
5467
}

0 commit comments

Comments
 (0)