Skip to content

Commit c7df239

Browse files
Add get stream method for client
1 parent 0c6eaf6 commit c7df239

File tree

14 files changed

+198
-80
lines changed

14 files changed

+198
-80
lines changed

ManagedCode.Storage.Aws/AWSStorage.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using ManagedCode.Storage.Core;
1414
using ManagedCode.Storage.Core.Models;
1515
using Microsoft.Extensions.Logging;
16+
using Microsoft.Extensions.Options;
1617

1718
namespace ManagedCode.Storage.Aws;
1819

@@ -90,6 +91,13 @@ public override async IAsyncEnumerable<BlobMetadata> GetBlobMetadataListAsync(st
9091
} while (objectsRequest is not null);
9192
}
9293

94+
public override async Task<Result<Stream>> GetStreamAsync(string fileName, CancellationToken cancellationToken = default)
95+
{
96+
Stream stream = await StorageClient.GetObjectStreamAsync(StorageOptions.Bucket, fileName, null,
97+
cancellationToken);
98+
return Result<Stream>.Succeed(stream);
99+
}
100+
93101
protected override IAmazonS3 CreateStorageClient()
94102
{
95103
return new AmazonS3Client(new BasicAWSCredentials(StorageOptions.PublicKey, StorageOptions.SecretKey), StorageOptions.OriginalOptions);

ManagedCode.Storage.Azure.DataLake/AzureDataLakeStorage.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,16 @@ private DataLakeFileClient GetFileClient(BaseOptions options)
277277
_ => StorageClient.GetDirectoryClient(options.Directory).GetFileClient(options.FileName)
278278
};
279279
}
280+
281+
public override async Task<Result<Stream>> GetStreamAsync(string fileName, CancellationToken cancellationToken = default)
282+
{
283+
return await OpenReadStreamAsync(
284+
new OpenReadStreamOptions()
285+
{
286+
FileName = fileName,
287+
Position = 0,
288+
BufferSize = 4096
289+
},
290+
cancellationToken);
291+
}
280292
}

ManagedCode.Storage.Azure/AzureStorage.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,4 +374,9 @@ public async Task<Result> SetStorageOptions(Action<IStorageOptions> options,
374374

375375
return await CreateContainerAsync(cancellationToken);
376376
}
377+
378+
public async override Task<Result<Stream>> GetStreamAsync(string fileName, CancellationToken cancellationToken = default)
379+
{
380+
return await OpenReadStreamAsync(fileName, cancellationToken);
381+
}
377382
}

ManagedCode.Storage.Client/IStorageClient.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace ManagedCode.Storage.Client;
1010

11-
public interface IStorageClient : IUploader, IDownloader
11+
public interface IStorageClient
1212
{
1313
void SetChunkSize(long size);
1414

@@ -20,6 +20,24 @@ public interface IStorageClient : IUploader, IDownloader
2020
/// This includes the file name, progress percentage, total bytes, transferred bytes, elapsed time, remaining time, speed, and any error message.
2121
/// </remarks>
2222
event EventHandler<ProgressStatus> OnProgressStatusChanged;
23+
24+
Task<Result<BlobMetadata>> UploadFile(string base64, string apiUrl, string contentName, CancellationToken cancellationToken = default);
25+
26+
Task<Result<BlobMetadata>> UploadFile(byte[] bytes, string apiUrl, string contentName, CancellationToken cancellationToken = default);
27+
28+
Task<Result<BlobMetadata>> UploadFile(FileInfo fileInfo, string apiUrl, string contentName, CancellationToken cancellationToken = default);
29+
30+
Task<Result<BlobMetadata>> UploadFile(Stream stream, string apiUrl, string contentName, CancellationToken cancellationToken = default);
31+
32+
Task<Result<LocalFile>> DownloadFile(string fileName, string apiUrl, string? path = null, CancellationToken cancellationToken = default);
33+
34+
Task<Result<uint>> UploadLargeFile(Stream file,
35+
string uploadApiUrl,
36+
string completeApiUrl,
37+
Action<double>? onProgressChanged,
38+
CancellationToken cancellationToken = default);
39+
40+
Task<Result<Stream>> GetFileStream(string fileName, string apiUrl, CancellationToken cancellationToken = default);
2341
}
2442

2543

ManagedCode.Storage.Client/StorageClient.cs

Lines changed: 22 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
using System;
1+
using ManagedCode.Communication;
2+
using ManagedCode.Storage.Core.Models;
3+
using System;
24
using System.Collections.Generic;
35
using System.IO;
46
using System.Net;
57
using System.Net.Http;
68
using System.Net.Http.Json;
79
using System.Threading;
810
using System.Threading.Tasks;
9-
using ManagedCode.Communication;
10-
using ManagedCode.Storage.Core.Models;
1111

1212
namespace ManagedCode.Storage.Client;
1313

@@ -191,78 +191,26 @@ public async Task<Result<uint>> UploadLargeFile(Stream file,
191191
return await mergeResult.Content.ReadFromJsonAsync<Result<uint>>(cancellationToken: cancellationToken);
192192
}
193193

194-
public Task<Result<BlobMetadata>> UploadAsync(Stream stream, CancellationToken cancellationToken = default)
195-
{
196-
throw new NotImplementedException();
197-
}
198-
199-
public Task<Result<BlobMetadata>> UploadAsync(byte[] data, CancellationToken cancellationToken = default)
200-
{
201-
throw new NotImplementedException();
202-
}
203-
204-
public Task<Result<BlobMetadata>> UploadAsync(string content, CancellationToken cancellationToken = default)
205-
{
206-
throw new NotImplementedException();
207-
}
208-
209-
public Task<Result<BlobMetadata>> UploadAsync(FileInfo fileInfo, CancellationToken cancellationToken = default)
210-
{
211-
throw new NotImplementedException();
212-
}
213-
214-
public Task<Result<BlobMetadata>> UploadAsync(Stream stream, UploadOptions options, CancellationToken cancellationToken = default)
194+
public async Task<Result<Stream>> GetFileStream(string fileName, string apiUrl, CancellationToken cancellationToken = default)
215195
{
216-
throw new NotImplementedException();
217-
}
218-
219-
public Task<Result<BlobMetadata>> UploadAsync(byte[] data, UploadOptions options, CancellationToken cancellationToken = default)
220-
{
221-
throw new NotImplementedException();
222-
}
223-
224-
public Task<Result<BlobMetadata>> UploadAsync(string content, UploadOptions options, CancellationToken cancellationToken = default)
225-
{
226-
throw new NotImplementedException();
227-
}
228-
229-
public Task<Result<BlobMetadata>> UploadAsync(FileInfo fileInfo, UploadOptions options, CancellationToken cancellationToken = default)
230-
{
231-
throw new NotImplementedException();
232-
}
233-
234-
public Task<Result<BlobMetadata>> UploadAsync(Stream stream, Action<UploadOptions> action, CancellationToken cancellationToken = default)
235-
{
236-
throw new NotImplementedException();
237-
}
238-
239-
public Task<Result<BlobMetadata>> UploadAsync(byte[] data, Action<UploadOptions> action, CancellationToken cancellationToken = default)
240-
{
241-
throw new NotImplementedException();
242-
}
243-
244-
public Task<Result<BlobMetadata>> UploadAsync(string content, Action<UploadOptions> action, CancellationToken cancellationToken = default)
245-
{
246-
throw new NotImplementedException();
247-
}
248-
249-
public Task<Result<BlobMetadata>> UploadAsync(FileInfo fileInfo, Action<UploadOptions> action, CancellationToken cancellationToken = default)
250-
{
251-
throw new NotImplementedException();
252-
}
253-
254-
public Task<Result<LocalFile>> DownloadAsync(string fileName, CancellationToken cancellationToken = default)
255-
{
256-
throw new NotImplementedException();
257-
}
258-
259-
public Task<Result<LocalFile>> DownloadAsync(DownloadOptions options, CancellationToken cancellationToken = default)
260-
{
261-
throw new NotImplementedException();
262-
}
196+
try
197+
{
198+
var response = await _httpClient.GetAsync($"{apiUrl}/{fileName}");
199+
if (response.IsSuccessStatusCode)
200+
{
201+
var stream = await response.Content.ReadAsStreamAsync();
202+
return Result<Stream>.Succeed(stream);
203+
}
263204

264-
public Task<Result<LocalFile>> DownloadAsync(Action<DownloadOptions> action, CancellationToken cancellationToken = default)
265-
{
266-
throw new NotImplementedException();
205+
return Result<Stream>.Fail(response.StatusCode);
206+
}
207+
catch (HttpRequestException e) when (e.StatusCode != null)
208+
{
209+
return Result<Stream>.Fail(e.StatusCode.Value);
210+
}
211+
catch (Exception)
212+
{
213+
return Result<Stream>.Fail(HttpStatusCode.InternalServerError);
214+
}
267215
}
268216
}

ManagedCode.Storage.Core/BaseStorage.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ public Task<Result> SetStorageOptions(Action<TOptions> options, CancellationToke
261261
return CreateContainerAsync(cancellationToken);
262262
}
263263

264+
public abstract Task<Result<Stream>> GetStreamAsync(string fileName, CancellationToken cancellationToken = default);
265+
264266
public T StorageClient { get; protected set; }
265267

266268
protected abstract T CreateStorageClient();

ManagedCode.Storage.Core/IStorage.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ public interface IStorage<out T, TOptions> : IStorage where TOptions : IStorageO
1515
Task<Result> SetStorageOptions(Action<TOptions> options, CancellationToken cancellationToken = default);
1616
}
1717

18-
1918
public interface IDownloader
2019
{
2120
/// <summary>
@@ -34,6 +33,14 @@ public interface IDownloader
3433
Task<Result<LocalFile>> DownloadAsync(Action<DownloadOptions> action, CancellationToken cancellationToken = default);
3534
}
3635

36+
public interface IStreamer
37+
{
38+
/// <summary>
39+
/// Gets file stream.
40+
/// </summary>
41+
Task<Result<Stream>> GetStreamAsync(string fileName, CancellationToken cancellationToken = default);
42+
}
43+
3744
public interface IUploader
3845
{
3946
/// <summary>
@@ -186,7 +193,7 @@ public interface IStorageOperations
186193
Task<Result<bool>> HasLegalHoldAsync(Action<LegalHoldOptions> action, CancellationToken cancellationToken = default);
187194
}
188195

189-
public interface IStorage : IUploader, IDownloader, IStorageOperations
196+
public interface IStorage : IUploader, IDownloader, IStreamer, IStorageOperations
190197
{
191198
/// <summary>
192199
/// Create a container if it does not already exist.

ManagedCode.Storage.FileSystem/FileSystemStorage.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using ManagedCode.Storage.Core;
1111
using ManagedCode.Storage.Core.Models;
1212
using ManagedCode.Storage.FileSystem.Options;
13+
using Microsoft.Extensions.Options;
1314

1415
namespace ManagedCode.Storage.FileSystem;
1516

@@ -236,4 +237,15 @@ private void EnsureDirectoryExist(string directory)
236237
Directory.CreateDirectory(path);
237238
}
238239
}
240+
241+
public override async Task<Result<Stream>> GetStreamAsync(string fileName, CancellationToken cancellationToken = default)
242+
{
243+
await EnsureContainerExist();
244+
245+
var filePath = GetPathFromOptions(new DownloadOptions() { FileName = fileName });
246+
247+
return File.Exists(filePath)
248+
? Result<Stream>.Succeed(new FileStream(filePath, FileMode.Open, FileAccess.Read))
249+
: Result<Stream>.Fail("File not found");
250+
}
239251
}

ManagedCode.Storage.Google/GCPStorage.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System.IO;
55
using System.Linq;
66
using System.Net;
7+
using System.Net.Http;
8+
using System.Security.AccessControl;
79
using System.Threading;
810
using System.Threading.Tasks;
911
using Google;
@@ -19,10 +21,15 @@ namespace ManagedCode.Storage.Google;
1921
public class GCPStorage : BaseStorage<StorageClient, GCPStorageOptions>, IGCPStorage
2022
{
2123
private readonly ILogger<GCPStorage>? _logger;
24+
private UrlSigner urlSigner;
2225

2326
public GCPStorage(GCPStorageOptions options, ILogger<GCPStorage>? logger = null) : base(options)
2427
{
2528
_logger = logger;
29+
if (options.GoogleCredential != null)
30+
{
31+
urlSigner = UrlSigner.FromCredential(options.GoogleCredential);
32+
}
2633
}
2734

2835
public override async Task<Result> RemoveContainerAsync(CancellationToken cancellationToken = default)
@@ -278,4 +285,22 @@ protected override async Task<Result<bool>> HasLegalHoldInternalAsync(LegalHoldO
278285
return Result<bool>.Fail(ex);
279286
}
280287
}
288+
289+
public override async Task<Result<Stream>> GetStreamAsync(string fileName, CancellationToken cancellationToken = default)
290+
{
291+
await EnsureContainerExist();
292+
293+
if (urlSigner == null)
294+
{
295+
return Result<Stream>.Fail("Google credentials are required to get stream");
296+
}
297+
298+
string signedUrl = urlSigner.Sign(StorageOptions.BucketOptions.Bucket, fileName, TimeSpan.FromHours(1), HttpMethod.Get);
299+
300+
using (HttpClient httpClient = new HttpClient())
301+
{
302+
Stream stream = await httpClient.GetStreamAsync(signedUrl, cancellationToken);
303+
return Result<Stream>.Succeed(stream);
304+
}
305+
}
281306
}

ManagedCode.Storage.IntegrationTests/Constants/ApiEndpoints.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ public static class Base
99
public const string UploadFile = "{0}/upload";
1010
public const string UploadLargeFile = "{0}/upload-chunks";
1111
public const string DownloadFile = "{0}/download";
12+
public const string StreamFile = "{0}/stream";
1213
}
1314
}

0 commit comments

Comments
 (0)