From 82e4d9b480c74c0bc2be4b9b3595fff2ea768c1d Mon Sep 17 00:00:00 2001 From: Mauro van der Gun Date: Wed, 9 Aug 2023 13:49:39 -0400 Subject: [PATCH] add stream support --- .../src/AmazonS3Adapter.cs | 64 ++-------- .../src/AzureBlobStorageAdapter.cs | 58 +-------- .../src/AzureFileStorageAdapter.cs | 74 ++--------- .../src/DropboxAdapter.cs | 46 +------ .../src/MicrosoftOneDriveAdapter.cs | 73 +---------- FileSystem.Adapters.Sftp/src/SftpAdapter.cs | 52 +++----- FileSystem/FileSystem.csproj | 1 + FileSystem/src/Adapters/Adapter.cs | 80 +++++++++++- FileSystem/src/Adapters/IAdapter.cs | 4 + FileSystem/src/Adapters/LocalAdapter.cs | 32 ++--- FileSystem/src/Constants/AdapterConstants.cs | 7 ++ FileSystem/src/FileSystem.cs | 119 ++++++++++++++---- FileSystem/src/IFileSystem.cs | 95 ++++++++++---- FileSystem/src/Utilities/StreamUtilities.cs | 19 +++ 14 files changed, 337 insertions(+), 387 deletions(-) create mode 100644 FileSystem/src/Constants/AdapterConstants.cs create mode 100644 FileSystem/src/Utilities/StreamUtilities.cs diff --git a/FileSystem.Adapters.AmazonS3/src/AmazonS3Adapter.cs b/FileSystem.Adapters.AmazonS3/src/AmazonS3Adapter.cs index 67c33f0..57af747 100644 --- a/FileSystem.Adapters.AmazonS3/src/AmazonS3Adapter.cs +++ b/FileSystem.Adapters.AmazonS3/src/AmazonS3Adapter.cs @@ -224,18 +224,16 @@ public override async Task DeleteFileAsync(string virtualPath, CancellationToken } } - public override async Task ReadFileAsync(string virtualPath, CancellationToken cancellationToken = default) + public override async Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default) { await GetFileAsync(virtualPath, cancellationToken); var path = GetPath(virtualPath); try { - using var response = await client.GetObjectAsync(bucketName, path, cancellationToken); - using var memoryStream = new MemoryStream(); - await response.ResponseStream.CopyToAsync(memoryStream, 81920, cancellationToken); + var response = await client.GetObjectAsync(bucketName, path, cancellationToken); - return memoryStream.ToArray(); + return response.ResponseStream; } catch (Exception exception) { @@ -243,29 +241,7 @@ public override async Task ReadFileAsync(string virtualPath, Cancellatio } } - public override async Task ReadTextFileAsync(string virtualPath, CancellationToken cancellationToken = default) - { - await GetFileAsync(virtualPath, cancellationToken); - var path = GetPath(virtualPath); - - try - { - using var response = await client.GetObjectAsync(bucketName, path, cancellationToken); - using var memoryStream = new MemoryStream(); - await response.ResponseStream.CopyToAsync(memoryStream, 81920, cancellationToken); - - using var streamReader = new StreamReader(memoryStream); - memoryStream.Position = 0; - - return await streamReader.ReadToEndAsync(); - } - catch (Exception exception) - { - throw Exception(exception); - } - } - - public override async Task WriteFileAsync(string virtualPath, byte[] contents, bool overwrite = false, CancellationToken cancellationToken = default) + public override async Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default) { if (!overwrite && await FileExistsAsync(virtualPath, cancellationToken)) { @@ -276,37 +252,11 @@ public override async Task WriteFileAsync(string virtualPath, byte[] contents, b try { - using var memoryStream = new MemoryStream(contents); - var request = new PutObjectRequest - { - InputStream = memoryStream, - BucketName = bucketName, - Key = path - }; - - await client.PutObjectAsync(request, cancellationToken); - } - catch (Exception exception) - { - throw Exception(exception); - } - } - - public override async Task AppendFileAsync(string virtualPath, byte[] contents, CancellationToken cancellationToken = default) - { - await GetFileAsync(virtualPath, cancellationToken); - var existingContents = await ReadFileAsync(virtualPath, cancellationToken); - contents = existingContents.Concat(contents).ToArray(); - await DeleteFileAsync(virtualPath, cancellationToken); + contents.Seek(0, SeekOrigin.Begin); - var path = GetPath(virtualPath); - - try - { - using var memoryStream = new MemoryStream(contents); var request = new PutObjectRequest { - InputStream = memoryStream, + InputStream = contents, BucketName = bucketName, Key = path }; @@ -319,7 +269,7 @@ public override async Task AppendFileAsync(string virtualPath, byte[] contents, } } - private static Exception Exception(Exception exception) + protected override Exception Exception(Exception exception) { if (exception is FileSystemException) { diff --git a/FileSystem.Adapters.AzureBlobStorage/src/AzureBlobStorageAdapter.cs b/FileSystem.Adapters.AzureBlobStorage/src/AzureBlobStorageAdapter.cs index 84916c3..0d74a80 100644 --- a/FileSystem.Adapters.AzureBlobStorage/src/AzureBlobStorageAdapter.cs +++ b/FileSystem.Adapters.AzureBlobStorage/src/AzureBlobStorageAdapter.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Azure; @@ -203,38 +202,14 @@ public override async Task DeleteFileAsync(string virtualPath, CancellationToken } } - public override async Task ReadFileAsync(string virtualPath, CancellationToken cancellationToken = default) + public override async Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default) { await GetFileAsync(virtualPath, cancellationToken); var path = GetPath(virtualPath); try { - using var memoryStream = new MemoryStream(); - await client.GetBlobClient(path).DownloadToAsync(memoryStream, cancellationToken); - - return memoryStream.ToArray(); - } - catch (Exception exception) - { - throw Exception(exception); - } - } - - public override async Task ReadTextFileAsync(string virtualPath, CancellationToken cancellationToken = default) - { - await GetFileAsync(virtualPath, cancellationToken); - var path = GetPath(virtualPath); - - try - { - using var memoryStream = new MemoryStream(); - await client.GetBlobClient(path).DownloadToAsync(memoryStream, cancellationToken); - - using var streamReader = new StreamReader(memoryStream); - memoryStream.Position = 0; - - return await streamReader.ReadToEndAsync(); + return await client.GetBlobClient(path).OpenReadAsync(cancellationToken: cancellationToken); } catch (Exception exception) { @@ -242,7 +217,7 @@ public override async Task ReadTextFileAsync(string virtualPath, Cancell } } - public override async Task WriteFileAsync(string virtualPath, byte[] contents, bool overwrite = false, CancellationToken cancellationToken = default) + public override async Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default) { if (!overwrite && await FileExistsAsync(virtualPath, cancellationToken)) { @@ -253,30 +228,9 @@ public override async Task WriteFileAsync(string virtualPath, byte[] contents, b try { - using var memoryStream = new MemoryStream(contents); - - await client.UploadBlobAsync(path, memoryStream, cancellationToken); - } - catch (Exception exception) - { - throw Exception(exception); - } - } - - public override async Task AppendFileAsync(string virtualPath, byte[] contents, CancellationToken cancellationToken = default) - { - await GetFileAsync(virtualPath, cancellationToken); - var existingContents = await ReadFileAsync(virtualPath, cancellationToken); - contents = existingContents.Concat(contents).ToArray(); - await DeleteFileAsync(virtualPath, cancellationToken); - - var path = GetPath(virtualPath); - - try - { - using var memoryStream = new MemoryStream(contents); + contents.Seek(0, SeekOrigin.Begin); - await client.UploadBlobAsync(path, memoryStream, cancellationToken); + await client.UploadBlobAsync(path, contents, cancellationToken); } catch (Exception exception) { @@ -284,7 +238,7 @@ public override async Task AppendFileAsync(string virtualPath, byte[] contents, } } - private static Exception Exception(Exception exception) + protected override Exception Exception(Exception exception) { if (exception is FileSystemException) { diff --git a/FileSystem.Adapters.AzureFileStorage/src/AzureFileStorageAdapter.cs b/FileSystem.Adapters.AzureFileStorage/src/AzureFileStorageAdapter.cs index 054cf19..a5068f8 100644 --- a/FileSystem.Adapters.AzureFileStorage/src/AzureFileStorageAdapter.cs +++ b/FileSystem.Adapters.AzureFileStorage/src/AzureFileStorageAdapter.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Azure; @@ -185,7 +184,7 @@ public override async Task DeleteFileAsync(string virtualPath, CancellationToken } } - public override async Task ReadFileAsync(string virtualPath, CancellationToken cancellationToken = default) + public override async Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default) { await GetFileAsync(virtualPath, cancellationToken); @@ -198,37 +197,7 @@ public override async Task ReadFileAsync(string virtualPath, Cancellatio var directory = client.GetDirectoryClient(directoryPath); var download = await directory.GetFileClient(filePath).DownloadAsync(cancellationToken: cancellationToken); - using var memoryStream = new MemoryStream(); - await download.Value.Content.CopyToAsync(memoryStream, 81920, cancellationToken); - - return memoryStream.ToArray(); - } - catch (Exception exception) - { - throw Exception(exception); - } - } - - public override async Task ReadTextFileAsync(string virtualPath, CancellationToken cancellationToken = default) - { - await GetFileAsync(virtualPath, cancellationToken); - - var path = GetPath(virtualPath); - var filePath = GetLastPathPart(path); - var directoryPath = GetParentPathPart(path); - - try - { - var directory = client.GetDirectoryClient(directoryPath); - var file = directory.GetFileClient(filePath); - var download = await file.DownloadAsync(cancellationToken: cancellationToken); - - using var memoryStream = new MemoryStream(); - await download.Value.Content.CopyToAsync(memoryStream, 81920, cancellationToken); - using var streamReader = new StreamReader(memoryStream); - memoryStream.Position = 0; - - return await streamReader.ReadToEndAsync(); + return download.Value.Content; } catch (Exception exception) { @@ -236,7 +205,7 @@ public override async Task ReadTextFileAsync(string virtualPath, Cancell } } - public override async Task WriteFileAsync(string virtualPath, byte[] contents, bool overwrite = false, CancellationToken cancellationToken = default) + public override async Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default) { if (!overwrite && await FileExistsAsync(virtualPath, cancellationToken)) { @@ -253,39 +222,10 @@ public override async Task WriteFileAsync(string virtualPath, byte[] contents, b await directory.CreateIfNotExistsAsync(cancellationToken: cancellationToken); var file = directory.GetFileClient(filePath); - using var memoryStream = new MemoryStream(contents); - await file.CreateAsync(memoryStream.Length, cancellationToken: cancellationToken); - - await file.UploadRangeAsync(new HttpRange(0, memoryStream.Length), memoryStream, cancellationToken: cancellationToken); - } - catch (Exception exception) - { - throw Exception(exception); - } - } - - public override async Task AppendFileAsync(string virtualPath, byte[] contents, CancellationToken cancellationToken = default) - { - await GetFileAsync(virtualPath, cancellationToken); - var existingContents = await ReadFileAsync(virtualPath, cancellationToken); - - var path = GetPath(virtualPath); - var filePath = GetLastPathPart(path); - var directoryPath = GetParentPathPart(path); - - try - { - var directory = client.GetDirectoryClient(directoryPath); - var file = directory.GetFileClient(filePath); - - contents = existingContents.Concat(contents).ToArray(); - - using var memoryStream = new MemoryStream(contents); - - await file.DeleteAsync(cancellationToken); - await file.CreateAsync(memoryStream.Length, cancellationToken: cancellationToken); + contents.Seek(0, SeekOrigin.Begin); - await file.UploadRangeAsync(new HttpRange(0, memoryStream.Length), memoryStream, cancellationToken: cancellationToken); + await file.CreateAsync(contents.Length, cancellationToken: cancellationToken); + await file.UploadRangeAsync(new HttpRange(0, contents.Length), contents, cancellationToken: cancellationToken); } catch (Exception exception) { @@ -293,7 +233,7 @@ public override async Task AppendFileAsync(string virtualPath, byte[] contents, } } - private static Exception Exception(Exception exception) + protected override Exception Exception(Exception exception) { if (exception is FileSystemException) { diff --git a/FileSystem.Adapters.Dropbox/src/DropboxAdapter.cs b/FileSystem.Adapters.Dropbox/src/DropboxAdapter.cs index e6d2e50..3e1dc26 100644 --- a/FileSystem.Adapters.Dropbox/src/DropboxAdapter.cs +++ b/FileSystem.Adapters.Dropbox/src/DropboxAdapter.cs @@ -170,7 +170,7 @@ public override async Task DeleteDirectoryAsync(string virtualPath, Cancellation } } - public override async Task ReadFileAsync(string virtualPath, CancellationToken cancellationToken = default) + public override async Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default) { await GetFileAsync(virtualPath, cancellationToken); @@ -178,7 +178,7 @@ public override async Task ReadFileAsync(string virtualPath, Cancellatio { using var response = await client.Files.DownloadAsync(GetPath(virtualPath)); - return await response.GetContentAsByteArrayAsync(); + return await response.GetContentAsStreamAsync(); } catch (Exception exception) { @@ -186,23 +186,7 @@ public override async Task ReadFileAsync(string virtualPath, Cancellatio } } - public override async Task ReadTextFileAsync(string virtualPath, CancellationToken cancellationToken = default) - { - await GetFileAsync(virtualPath, cancellationToken); - - try - { - using var response = await client.Files.DownloadAsync(GetPath(virtualPath)); - - return await response.GetContentAsStringAsync(); - } - catch (Exception exception) - { - throw Exception(exception); - } - } - - public override async Task WriteFileAsync(string virtualPath, byte[] contents, bool overwrite = false, CancellationToken cancellationToken = default) + public override async Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default) { if (!overwrite && await FileExistsAsync(virtualPath, cancellationToken)) { @@ -211,27 +195,9 @@ public override async Task WriteFileAsync(string virtualPath, byte[] contents, b try { - using var memoryStream = new MemoryStream(contents); - - await client.Files.UploadAsync(GetPath(virtualPath), WriteMode.Overwrite.Instance, body: memoryStream); - } - catch (Exception exception) - { - throw Exception(exception); - } - } - - public override async Task AppendFileAsync(string virtualPath, byte[] contents, CancellationToken cancellationToken = default) - { - await GetFileAsync(virtualPath, cancellationToken); - var existingContents = await ReadFileAsync(virtualPath, cancellationToken); - contents = existingContents.Concat(contents).ToArray(); - - try - { - using var memoryStream = new MemoryStream(contents); + contents.Seek(0, SeekOrigin.Begin); - await client.Files.UploadAsync(GetPath(virtualPath), WriteMode.Overwrite.Instance, body: memoryStream); + await client.Files.UploadAsync(GetPath(virtualPath), WriteMode.Overwrite.Instance, body: contents); } catch (Exception exception) { @@ -239,7 +205,7 @@ public override async Task AppendFileAsync(string virtualPath, byte[] contents, } } - private static Exception Exception(Exception exception) + protected override Exception Exception(Exception exception) { if (exception is FileSystemException) { diff --git a/FileSystem.Adapters.MicrosoftOneDrive/src/MicrosoftOneDriveAdapter.cs b/FileSystem.Adapters.MicrosoftOneDrive/src/MicrosoftOneDriveAdapter.cs index 4fe00ca..5340d29 100644 --- a/FileSystem.Adapters.MicrosoftOneDrive/src/MicrosoftOneDriveAdapter.cs +++ b/FileSystem.Adapters.MicrosoftOneDrive/src/MicrosoftOneDriveAdapter.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -235,44 +234,16 @@ public override async Task DeleteFileAsync(string virtualPath, CancellationToken } } - public override async Task ReadFileAsync(string virtualPath, CancellationToken cancellationToken = default) + public override async Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default) { await GetFileAsync(virtualPath, cancellationToken); var path = GetPath(virtualPath); try { - using var memoryStream = new MemoryStream(); var item = await GetItemAsync(path, cancellationToken); - var stream = await client.Drives[driveId].Items[item.Id].Content.Request().GetAsync(cancellationToken); - await stream.CopyToAsync(memoryStream, 81920, cancellationToken); - - return memoryStream.ToArray(); - } - catch (Exception exception) - { - throw Exception(exception); - } - } - - public override async Task ReadTextFileAsync(string virtualPath, CancellationToken cancellationToken = default) - { - await GetFileAsync(virtualPath, cancellationToken); - var path = GetPath(virtualPath); - - try - { - using var memoryStream = new MemoryStream(); - var item = await GetItemAsync(path, cancellationToken); - - var stream = await client.Drives[driveId].Items[item.Id].Content.Request().GetAsync(cancellationToken); - await stream.CopyToAsync(memoryStream, 81920, cancellationToken); - - using var streamReader = new StreamReader(memoryStream); - memoryStream.Position = 0; - - return await streamReader.ReadToEndAsync(); + return await client.Drives[driveId].Items[item.Id].Content.Request().GetAsync(cancellationToken); } catch (Exception exception) { @@ -280,7 +251,7 @@ public override async Task ReadTextFileAsync(string virtualPath, Cancell } } - public override async Task WriteFileAsync(string virtualPath, byte[] contents, bool overwrite = false, CancellationToken cancellationToken = default) + public override async Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default) { if (!overwrite && await FileExistsAsync(virtualPath, cancellationToken)) { @@ -291,41 +262,9 @@ public override async Task WriteFileAsync(string virtualPath, byte[] contents, b try { - using var memoryStream = new MemoryStream(contents); - var uploadSession = await client.Drives[driveId].Root.ItemWithPath(path).CreateUploadSession().Request().PostAsync(cancellationToken); - var largeFileUploadTask = new LargeFileUploadTask(uploadSession, memoryStream); - - var result = await largeFileUploadTask.UploadAsync(); - - if (!result.UploadSucceeded) - { - throw new AdapterRuntimeException(); - } - } - catch (TaskCanceledException exception) when (exception.InnerException != null) - { - throw Exception(exception.InnerException); - } - catch (Exception exception) - { - throw Exception(exception); - } - } - - public override async Task AppendFileAsync(string virtualPath, byte[] contents, CancellationToken cancellationToken = default) - { - await GetFileAsync(virtualPath, cancellationToken); - var existingContents = await ReadFileAsync(virtualPath, cancellationToken); - contents = existingContents.Concat(contents).ToArray(); - await DeleteFileAsync(virtualPath, cancellationToken); - - var path = GetPath(virtualPath); - - try - { - using var memoryStream = new MemoryStream(contents); + contents.Seek(0, SeekOrigin.Begin); var uploadSession = await client.Drives[driveId].Root.ItemWithPath(path).CreateUploadSession().Request().PostAsync(cancellationToken); - var largeFileUploadTask = new LargeFileUploadTask(uploadSession, memoryStream); + var largeFileUploadTask = new LargeFileUploadTask(uploadSession, contents); var result = await largeFileUploadTask.UploadAsync(); @@ -383,7 +322,7 @@ private async Task GetItemsAsync(string path, return await client.Drives[driveId].Root.ItemWithPath(path).Children.Request().GetAsync(cancellationToken); } - private static Exception Exception(Exception exception) + protected override Exception Exception(Exception exception) { if (exception is FileSystemException) { diff --git a/FileSystem.Adapters.Sftp/src/SftpAdapter.cs b/FileSystem.Adapters.Sftp/src/SftpAdapter.cs index c49515c..bfe2356 100644 --- a/FileSystem.Adapters.Sftp/src/SftpAdapter.cs +++ b/FileSystem.Adapters.Sftp/src/SftpAdapter.cs @@ -10,6 +10,7 @@ using Renci.SshNet.Common; using SharpGrip.FileSystem.Exceptions; using SharpGrip.FileSystem.Models; +using SharpGrip.FileSystem.Utilities; using DirectoryNotFoundException = SharpGrip.FileSystem.Exceptions.DirectoryNotFoundException; using FileNotFoundException = SharpGrip.FileSystem.Exceptions.FileNotFoundException; @@ -103,7 +104,8 @@ public override async Task> GetFilesAsync(string virtualPath try { - return await Task.Run(() => client.ListDirectory(path).Where(item => !item.IsDirectory).Select(file => ModelFactory.CreateFile(file, GetVirtualPath(file.FullName))).ToList(), cancellationToken); + return await Task.Run(() => client.ListDirectory(path).Where(item => !item.IsDirectory).Select(file => ModelFactory.CreateFile(file, GetVirtualPath(file.FullName))).ToList(), + cancellationToken); } catch (Exception exception) { @@ -118,7 +120,9 @@ public override async Task> GetDirectoriesAsync(string v try { - return await Task.Run(() => client.ListDirectory(path).Where(item => item.IsDirectory).Select(directory => ModelFactory.CreateDirectory(directory, GetVirtualPath(directory.FullName))).ToList(), cancellationToken); + return await Task.Run( + () => client.ListDirectory(path).Where(item => item.IsDirectory).Select(directory => ModelFactory.CreateDirectory(directory, GetVirtualPath(directory.FullName))).ToList(), + cancellationToken); } catch (Exception exception) { @@ -143,20 +147,6 @@ public override async Task CreateDirectoryAsync(string virtualPath, Cancellation } } - public override async Task DeleteFileAsync(string virtualPath, CancellationToken cancellationToken = default) - { - await GetFileAsync(virtualPath, cancellationToken); - - try - { - await Task.Run(() => client.DeleteFile(GetPath(virtualPath)), cancellationToken); - } - catch (Exception exception) - { - throw Exception(exception); - } - } - public override async Task DeleteDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default) { await GetDirectoryAsync(virtualPath, cancellationToken); @@ -171,18 +161,13 @@ public override async Task DeleteDirectoryAsync(string virtualPath, Cancellation } } - public override async Task ReadFileAsync(string virtualPath, CancellationToken cancellationToken = default) + public override async Task DeleteFileAsync(string virtualPath, CancellationToken cancellationToken = default) { await GetFileAsync(virtualPath, cancellationToken); try { - using var fileStream = client.OpenRead(GetPath(virtualPath)); - var fileContents = new byte[fileStream.Length]; - - _ = await fileStream.ReadAsync(fileContents, 0, (int) fileStream.Length, cancellationToken); - - return fileContents; + await Task.Run(() => client.DeleteFile(GetPath(virtualPath)), cancellationToken); } catch (Exception exception) { @@ -190,15 +175,13 @@ public override async Task ReadFileAsync(string virtualPath, Cancellatio } } - public override async Task ReadTextFileAsync(string virtualPath, CancellationToken cancellationToken = default) + public override async Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default) { await GetFileAsync(virtualPath, cancellationToken); try { - using var streamReader = new StreamReader(client.OpenRead(GetPath(virtualPath))); - - return await streamReader.ReadToEndAsync(); + return client.OpenRead(GetPath(virtualPath)); } catch (Exception exception) { @@ -206,7 +189,7 @@ public override async Task ReadTextFileAsync(string virtualPath, Cancell } } - public override async Task WriteFileAsync(string virtualPath, byte[] contents, bool overwrite = false, CancellationToken cancellationToken = default) + public override async Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default) { if (!overwrite && await FileExistsAsync(virtualPath, cancellationToken)) { @@ -215,7 +198,10 @@ public override async Task WriteFileAsync(string virtualPath, byte[] contents, b try { - await Task.Run(() => client.WriteAllBytes(GetPath(virtualPath), contents), cancellationToken); + contents.Seek(0, SeekOrigin.Begin); + + var writeStream = client.OpenWrite(GetPath(virtualPath)); + await contents.CopyToAsync(writeStream); } catch (Exception exception) { @@ -223,13 +209,15 @@ public override async Task WriteFileAsync(string virtualPath, byte[] contents, b } } - public override async Task AppendFileAsync(string virtualPath, byte[] contents, CancellationToken cancellationToken = default) + public new async Task AppendFileAsync(string virtualPath, Stream contents, CancellationToken cancellationToken = default) { await GetFileAsync(virtualPath, cancellationToken); try { - var stringContents = Encoding.UTF8.GetString(contents, 0, contents.Length); + using var memoryStream = await StreamUtilities.CopyContentsToMemoryStreamAsync(contents, cancellationToken); + var fileContents = memoryStream.ToArray(); + var stringContents = Encoding.UTF8.GetString(fileContents, 0, fileContents.Length); await Task.Run(() => client.AppendAllText(GetPath(virtualPath), stringContents), cancellationToken); } @@ -243,7 +231,7 @@ public override async Task AppendFileAsync(string virtualPath, byte[] contents, } } - private static Exception Exception(Exception exception) + protected override Exception Exception(Exception exception) { if (exception is FileSystemException) { diff --git a/FileSystem/FileSystem.csproj b/FileSystem/FileSystem.csproj index 9f29a15..ab39496 100644 --- a/FileSystem/FileSystem.csproj +++ b/FileSystem/FileSystem.csproj @@ -16,4 +16,5 @@ + diff --git a/FileSystem/src/Adapters/Adapter.cs b/FileSystem/src/Adapters/Adapter.cs index 4005ea1..b39a18c 100644 --- a/FileSystem/src/Adapters/Adapter.cs +++ b/FileSystem/src/Adapters/Adapter.cs @@ -1,13 +1,16 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -using SharpGrip.FileSystem.Exceptions; +using SharpGrip.FileSystem.Constants; using SharpGrip.FileSystem.Extensions; using SharpGrip.FileSystem.Models; using SharpGrip.FileSystem.Utilities; +using DirectoryNotFoundException = SharpGrip.FileSystem.Exceptions.DirectoryNotFoundException; +using FileNotFoundException = SharpGrip.FileSystem.Exceptions.FileNotFoundException; namespace SharpGrip.FileSystem.Adapters { @@ -100,6 +103,43 @@ public byte[] ReadFile(string virtualPath) return ReadFileAsync(virtualPath).Result; } + public async Task ReadFileAsync(string virtualPath, CancellationToken cancellationToken = default) + { + await GetFileAsync(virtualPath, cancellationToken); + + try + { + using var stream = await ReadFileStreamAsync(virtualPath, cancellationToken); + using var memoryStream = await StreamUtilities.CopyContentsToMemoryStreamAsync(stream, cancellationToken); + + await stream.CopyToAsync(memoryStream, AdapterConstants.DefaultMemoryStreamBufferSize, cancellationToken); + + return memoryStream.ToArray(); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public async Task ReadTextFileAsync(string virtualPath, CancellationToken cancellationToken = default) + { + await GetFileAsync(virtualPath, cancellationToken); + + try + { + using var stream = await ReadFileStreamAsync(virtualPath, cancellationToken); + using var streamReader = new StreamReader(stream); + stream.Position = 0; + + return await streamReader.ReadToEndAsync(); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + public string ReadTextFile(string virtualPath) { return ReadTextFileAsync(virtualPath).Result; @@ -110,6 +150,11 @@ public void WriteFile(string virtualPath, byte[] contents, bool overwrite = fals WriteFileAsync(virtualPath, contents, overwrite).Wait(); } + public async Task WriteFileAsync(string virtualPath, byte[] contents, bool overwrite = false, CancellationToken cancellationToken = default) + { + await WriteFileAsync(virtualPath, new MemoryStream(contents), overwrite, cancellationToken); + } + public void WriteFile(string virtualPath, string contents, bool overwrite = false) { WriteFileAsync(virtualPath, contents, overwrite).Wait(); @@ -120,11 +165,37 @@ public async Task WriteFileAsync(string virtualPath, string contents, bool overw await WriteFileAsync(virtualPath, Encoding.UTF8.GetBytes(contents), overwrite, cancellationToken); } + public virtual async Task AppendFileAsync(string virtualPath, Stream contents, CancellationToken cancellationToken = default) + { + await GetFileAsync(virtualPath, cancellationToken); + + var memoryStream = await StreamUtilities.CopyContentsToMemoryStreamAsync(contents, cancellationToken); + + var existingContents = await ReadFileAsync(virtualPath, cancellationToken); + var fileContents = existingContents.Concat(memoryStream.ToArray()).ToArray(); + + await DeleteFileAsync(virtualPath, cancellationToken); + + try + { + await WriteFileAsync(virtualPath, fileContents, true, cancellationToken); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + public void AppendFile(string virtualPath, byte[] contents) { AppendFileAsync(virtualPath, contents).Wait(); } + public async Task AppendFileAsync(string virtualPath, byte[] contents, CancellationToken cancellationToken = default) + { + await AppendFileAsync(virtualPath, new MemoryStream(contents), cancellationToken); + } + public void AppendFile(string virtualPath, string contents) { AppendFileAsync(virtualPath, contents).Wait(); @@ -144,10 +215,9 @@ public async Task AppendFileAsync(string virtualPath, string contents, Cancellat public abstract Task CreateDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default); public abstract Task DeleteDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default); public abstract Task DeleteFileAsync(string virtualPath, CancellationToken cancellationToken = default); - public abstract Task ReadFileAsync(string virtualPath, CancellationToken cancellationToken = default); - public abstract Task ReadTextFileAsync(string virtualPath, CancellationToken cancellationToken = default); - public abstract Task WriteFileAsync(string virtualPath, byte[] contents, bool overwrite = false, CancellationToken cancellationToken = default); - public abstract Task AppendFileAsync(string virtualPath, byte[] contents, CancellationToken cancellationToken = default); + public abstract Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default); + public abstract Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default); + protected abstract Exception Exception(Exception exception); protected string GetPath(string path) { diff --git a/FileSystem/src/Adapters/IAdapter.cs b/FileSystem/src/Adapters/IAdapter.cs index ffd4f44..4546220 100644 --- a/FileSystem/src/Adapters/IAdapter.cs +++ b/FileSystem/src/Adapters/IAdapter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Threading; using System.Threading.Tasks; using SharpGrip.FileSystem.Models; @@ -30,13 +31,16 @@ public interface IAdapter : IDisposable void DeleteDirectory(string virtualPath); Task DeleteDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default); byte[] ReadFile(string virtualPath); + Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default); Task ReadFileAsync(string virtualPath, CancellationToken cancellationToken = default); string ReadTextFile(string virtualPath); Task ReadTextFileAsync(string virtualPath, CancellationToken cancellationToken = default); + Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default); void WriteFile(string virtualPath, byte[] contents, bool overwrite = false); Task WriteFileAsync(string virtualPath, byte[] contents, bool overwrite = false, CancellationToken cancellationToken = default); void WriteFile(string virtualPath, string contents, bool overwrite = false); Task WriteFileAsync(string virtualPath, string contents, bool overwrite = false, CancellationToken cancellationToken = default); + Task AppendFileAsync(string virtualPath, Stream contents, CancellationToken cancellationToken = default); void AppendFile(string virtualPath, byte[] contents); Task AppendFileAsync(string virtualPath, byte[] contents, CancellationToken cancellationToken = default); void AppendFile(string virtualPath, string contents); diff --git a/FileSystem/src/Adapters/LocalAdapter.cs b/FileSystem/src/Adapters/LocalAdapter.cs index 8fe5387..fe7c555 100644 --- a/FileSystem/src/Adapters/LocalAdapter.cs +++ b/FileSystem/src/Adapters/LocalAdapter.cs @@ -144,27 +144,14 @@ public override async Task DeleteDirectoryAsync(string virtualPath, Cancellation await Task.Run(() => Directory.Delete(GetPath(virtualPath), true), cancellationToken); } - public override async Task ReadFileAsync(string virtualPath, CancellationToken cancellationToken = default) + public override async Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default) { await GetFileAsync(virtualPath, cancellationToken); - using var fileStream = new FileStream(GetPath(virtualPath), FileMode.Open); - var fileContents = new byte[fileStream.Length]; - - _ = await fileStream.ReadAsync(fileContents, 0, (int) fileStream.Length, cancellationToken); - - return fileContents; - } - - public override async Task ReadTextFileAsync(string virtualPath, CancellationToken cancellationToken = default) - { - await GetFileAsync(virtualPath, cancellationToken); - using var streamReader = new StreamReader(GetPath(virtualPath)); - - return await streamReader.ReadToEndAsync(); + return new FileStream(GetPath(virtualPath), FileMode.Open); } - public override async Task WriteFileAsync(string virtualPath, byte[] contents, bool overwrite = false, CancellationToken cancellationToken = default) + public override async Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default) { if (!overwrite && await FileExistsAsync(virtualPath, cancellationToken)) { @@ -172,17 +159,24 @@ public override async Task WriteFileAsync(string virtualPath, byte[] contents, b } using var fileStream = new FileStream(GetPath(virtualPath), FileMode.Create); + contents.Seek(0, SeekOrigin.Begin); - await fileStream.WriteAsync(contents, 0, contents.Length, cancellationToken); + await contents.CopyToAsync(fileStream); } - public override async Task AppendFileAsync(string virtualPath, byte[] contents, CancellationToken cancellationToken = default) + public new async Task AppendFileAsync(string virtualPath, Stream contents, CancellationToken cancellationToken = default) { await GetFileAsync(virtualPath, cancellationToken); using var fileStream = new FileStream(GetPath(virtualPath), FileMode.Append); + contents.Seek(0, SeekOrigin.Begin); - await fileStream.WriteAsync(contents, 0, contents.Length, cancellationToken); + await contents.CopyToAsync(fileStream); + } + + protected override Exception Exception(Exception exception) + { + throw new NotImplementedException(); } } } \ No newline at end of file diff --git a/FileSystem/src/Constants/AdapterConstants.cs b/FileSystem/src/Constants/AdapterConstants.cs new file mode 100644 index 0000000..6b18777 --- /dev/null +++ b/FileSystem/src/Constants/AdapterConstants.cs @@ -0,0 +1,7 @@ +namespace SharpGrip.FileSystem.Constants +{ + public static class AdapterConstants + { + public const int DefaultMemoryStreamBufferSize = 81920; + } +} \ No newline at end of file diff --git a/FileSystem/src/FileSystem.cs b/FileSystem/src/FileSystem.cs index b048fb8..211d339 100644 --- a/FileSystem/src/FileSystem.cs +++ b/FileSystem/src/FileSystem.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -86,7 +87,7 @@ public IAdapter GetAdapter(string prefix) /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public IFile GetFile(string virtualPath) { return GetFileAsync(virtualPath).Result; @@ -104,7 +105,7 @@ public IFile GetFile(string virtualPath) /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public Task GetFileAsync(string virtualPath, CancellationToken cancellationToken = default) { var prefix = PathUtilities.GetPrefix(virtualPath); @@ -126,7 +127,7 @@ public Task GetFileAsync(string virtualPath, CancellationToken cancellati /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. public IDirectory GetDirectory(string virtualPath) { return GetDirectoryAsync(virtualPath).Result; @@ -144,7 +145,7 @@ public IDirectory GetDirectory(string virtualPath) /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. public Task GetDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default) { var prefix = PathUtilities.GetPrefix(virtualPath); @@ -166,7 +167,7 @@ public Task GetDirectoryAsync(string virtualPath, CancellationToken /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. public IEnumerable GetFiles(string virtualPath = "") { return GetFilesAsync(virtualPath).Result; @@ -184,7 +185,7 @@ public IEnumerable GetFiles(string virtualPath = "") /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. public Task> GetFilesAsync(string virtualPath = "", CancellationToken cancellationToken = default) { var prefix = PathUtilities.GetPrefix(virtualPath); @@ -206,7 +207,7 @@ public Task> GetFilesAsync(string virtualPath = "", Cancellat /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. public IEnumerable GetDirectories(string virtualPath = "") { return GetDirectoriesAsync(virtualPath).Result; @@ -224,7 +225,7 @@ public IEnumerable GetDirectories(string virtualPath = "") /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. public Task> GetDirectoriesAsync(string virtualPath = "", CancellationToken cancellationToken = default) { var prefix = PathUtilities.GetPrefix(virtualPath); @@ -359,7 +360,7 @@ public async Task CreateDirectoryAsync(string virtualPath, CancellationToken can /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public void DeleteFile(string virtualPath) { DeleteFileAsync(virtualPath).Wait(); @@ -376,7 +377,7 @@ public void DeleteFile(string virtualPath) /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public async Task DeleteFileAsync(string virtualPath, CancellationToken cancellationToken = default) { var prefix = PathUtilities.GetPrefix(virtualPath); @@ -397,7 +398,7 @@ public async Task DeleteFileAsync(string virtualPath, CancellationToken cancella /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. public void DeleteDirectory(string virtualPath) { DeleteDirectoryAsync(virtualPath).Wait(); @@ -414,7 +415,7 @@ public void DeleteDirectory(string virtualPath) /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. public async Task DeleteDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default) { var prefix = PathUtilities.GetPrefix(virtualPath); @@ -429,6 +430,7 @@ public async Task DeleteDirectoryAsync(string virtualPath, CancellationToken can /// Reads a file at the provided path. /// /// The virtual path (including prefix) where to read the file at. + /// Optional to propagate notifications that the operation should be cancelled. /// The file contents. /// Thrown when an exception occurs during the adapter's connection process. Contains an inner exception with more details. /// Thrown when an exception occurs during the adapter's runtime. Contains an inner exception with more details. @@ -436,7 +438,29 @@ public async Task DeleteDirectoryAsync(string virtualPath, CancellationToken can /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. + public async Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default) + { + var prefix = PathUtilities.GetPrefix(virtualPath); + var adapter = GetAdapter(prefix); + + adapter.Connect(); + + return await adapter.ReadFileStreamAsync(virtualPath, cancellationToken); + } + + /// + /// Reads a file at the provided path. + /// + /// The virtual path (including prefix) where to read the file at. + /// The file contents. + /// Thrown when an exception occurs during the adapter's connection process. Contains an inner exception with more details. + /// Thrown when an exception occurs during the adapter's runtime. Contains an inner exception with more details. + /// Thrown when no adapters are registered with the file system. + /// Thrown when multiple adapters are registered with the same prefix. + /// Thrown when an adapter could not be found via the provided prefix. + /// Thrown when a prefix in the provided path could not be found. + /// Thrown if the file does not exists at the given path. public byte[] ReadFile(string virtualPath) { return ReadFileAsync(virtualPath).Result; @@ -454,7 +478,7 @@ public byte[] ReadFile(string virtualPath) /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public async Task ReadFileAsync(string virtualPath, CancellationToken cancellationToken = default) { var prefix = PathUtilities.GetPrefix(virtualPath); @@ -476,7 +500,7 @@ public async Task ReadFileAsync(string virtualPath, CancellationToken ca /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public string ReadTextFile(string virtualPath) { return ReadTextFileAsync(virtualPath).Result; @@ -494,7 +518,7 @@ public string ReadTextFile(string virtualPath) /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public async Task ReadTextFileAsync(string virtualPath, CancellationToken cancellationToken = default) { var prefix = PathUtilities.GetPrefix(virtualPath); @@ -517,7 +541,7 @@ public async Task ReadTextFileAsync(string virtualPath, CancellationToke /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. /// Thrown if the file exists at the given path and parameter "overwrite" = false. public void CopyFile(string virtualSourcePath, string virtualDestinationPath, bool overwrite = false) { @@ -537,7 +561,7 @@ public void CopyFile(string virtualSourcePath, string virtualDestinationPath, bo /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. /// Thrown if the file exists at the given path and parameter "overwrite" = false. public async Task CopyFileAsync(string virtualSourcePath, string virtualDestinationPath, bool overwrite = false, CancellationToken cancellationToken = default) { @@ -566,7 +590,7 @@ public async Task CopyFileAsync(string virtualSourcePath, string virtualDestinat /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. /// Thrown if the file exists at the given path and parameter "overwrite" = false. public void MoveFile(string virtualSourcePath, string virtualDestinationPath, bool overwrite = false) { @@ -586,7 +610,7 @@ public void MoveFile(string virtualSourcePath, string virtualDestinationPath, bo /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. /// Thrown if the file exists at the given path and parameter "overwrite" = false. public async Task MoveFileAsync(string virtualSourcePath, string virtualDestinationPath, bool overwrite = false, CancellationToken cancellationToken = default) { @@ -604,6 +628,30 @@ public async Task MoveFileAsync(string virtualSourcePath, string virtualDestinat await sourceAdapter.DeleteFileAsync(virtualSourcePath, cancellationToken); } + /// + /// Writes stream contents to a file at the provided path. + /// + /// The virtual path (including prefix) where to write the byte array contents to. + /// The file byte array contents. + /// If a file at the destination path exists overwrite it. + /// Optional to propagate notifications that the operation should be cancelled. + /// Thrown when an exception occurs during the adapter's connection process. Contains an inner exception with more details. + /// Thrown when an exception occurs during the adapter's runtime. Contains an inner exception with more details. + /// Thrown when no adapters are registered with the file system. + /// Thrown when multiple adapters are registered with the same prefix. + /// Thrown when an adapter could not be found via the provided prefix. + /// Thrown when a prefix in the provided path could not be found. + /// Thrown if the file exists at the given path and parameter "overwrite" = false. + public async Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default) + { + var prefix = PathUtilities.GetPrefix(virtualPath); + var adapter = GetAdapter(prefix); + + adapter.Connect(); + + await adapter.WriteFileAsync(virtualPath, contents, overwrite, cancellationToken); + } + /// /// Writes byte array contents to a file at the provided path. /// @@ -688,6 +736,29 @@ public async Task WriteFileAsync(string virtualPath, string contents, bool overw await adapter.WriteFileAsync(virtualPath, contents, overwrite, cancellationToken); } + /// + /// Writes stream contents to a file at the provided path. + /// + /// The virtual path (including prefix) where to write the byte array contents to. + /// The file byte array contents. + /// Optional to propagate notifications that the operation should be cancelled. + /// Thrown when an exception occurs during the adapter's connection process. Contains an inner exception with more details. + /// Thrown when an exception occurs during the adapter's runtime. Contains an inner exception with more details. + /// Thrown when no adapters are registered with the file system. + /// Thrown when multiple adapters are registered with the same prefix. + /// Thrown when an adapter could not be found via the provided prefix. + /// Thrown when a prefix in the provided path could not be found. + /// Thrown if the file does not exists at the given path. + public async Task AppendFileAsync(string virtualPath, Stream contents, CancellationToken cancellationToken = default) + { + var prefix = PathUtilities.GetPrefix(virtualPath); + var adapter = GetAdapter(prefix); + + adapter.Connect(); + + await adapter.AppendFileAsync(virtualPath, contents, cancellationToken); + } + /// /// Writes byte array contents to a file at the provided path. /// @@ -699,7 +770,7 @@ public async Task WriteFileAsync(string virtualPath, string contents, bool overw /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public void AppendFile(string virtualPath, byte[] contents) { AppendFileAsync(virtualPath, contents).Wait(); @@ -717,7 +788,7 @@ public void AppendFile(string virtualPath, byte[] contents) /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public async Task AppendFileAsync(string virtualPath, byte[] contents, CancellationToken cancellationToken = default) { var prefix = PathUtilities.GetPrefix(virtualPath); @@ -739,7 +810,7 @@ public async Task AppendFileAsync(string virtualPath, byte[] contents, Cancellat /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public void AppendFile(string virtualPath, string contents) { AppendFileAsync(virtualPath, contents).Wait(); @@ -757,7 +828,7 @@ public void AppendFile(string virtualPath, string contents) /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public async Task AppendFileAsync(string virtualPath, string contents, CancellationToken cancellationToken = default) { var prefix = PathUtilities.GetPrefix(virtualPath); diff --git a/FileSystem/src/IFileSystem.cs b/FileSystem/src/IFileSystem.cs index bb2880c..a875272 100644 --- a/FileSystem/src/IFileSystem.cs +++ b/FileSystem/src/IFileSystem.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Threading; using System.Threading.Tasks; using SharpGrip.FileSystem.Adapters; @@ -37,7 +38,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. [Obsolete("Method is deprecated, please use the async version instead. Method will be removed in v2.0.")] public IFile GetFile(string virtualPath); @@ -53,7 +54,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public Task GetFileAsync(string virtualPath, CancellationToken cancellationToken = default); /// @@ -67,7 +68,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. [Obsolete("Method is deprecated, please use the async version instead. Method will be removed in v2.0.")] public IDirectory GetDirectory(string virtualPath); @@ -83,7 +84,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. public Task GetDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default); /// @@ -97,7 +98,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. [Obsolete("Method is deprecated, please use the async version instead. Method will be removed in v2.0.")] public IEnumerable GetFiles(string virtualPath = ""); @@ -113,7 +114,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. public Task> GetFilesAsync(string virtualPath = "", CancellationToken cancellationToken = default); /// @@ -127,7 +128,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. [Obsolete("Method is deprecated, please use the async version instead. Method will be removed in v2.0.")] public IEnumerable GetDirectories(string virtualPath = ""); @@ -143,7 +144,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. public Task> GetDirectoriesAsync(string virtualPath = "", CancellationToken cancellationToken = default); /// @@ -240,7 +241,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. [Obsolete("Method is deprecated, please use the async version instead. Method will be removed in v2.0.")] public void DeleteFile(string virtualPath); @@ -255,7 +256,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public Task DeleteFileAsync(string virtualPath, CancellationToken cancellationToken = default); /// @@ -268,7 +269,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. [Obsolete("Method is deprecated, please use the async version instead. Method will be removed in v2.0.")] public void DeleteDirectory(string virtualPath); @@ -283,9 +284,24 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the directory does not exists at the given path. + /// Thrown if the directory does not exists at the given path. public Task DeleteDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default); + /// + /// Reads a file at the provided path. + /// + /// The virtual path (including prefix) where to read the file at. + /// Optional to propagate notifications that the operation should be cancelled. + /// The file contents. + /// Thrown when an exception occurs during the adapter's connection process. Contains an inner exception with more details. + /// Thrown when an exception occurs during the adapter's runtime. Contains an inner exception with more details. + /// Thrown when no adapters are registered with the file system. + /// Thrown when multiple adapters are registered with the same prefix. + /// Thrown when an adapter could not be found via the provided prefix. + /// Thrown when a prefix in the provided path could not be found. + /// Thrown if the file does not exists at the given path. + public Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default); + /// /// Reads a file at the provided path. /// @@ -297,7 +313,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. [Obsolete("Method is deprecated, please use the async version instead. Method will be removed in v2.0.")] public byte[] ReadFile(string virtualPath); @@ -313,7 +329,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public Task ReadFileAsync(string virtualPath, CancellationToken cancellationToken = default); /// @@ -327,7 +343,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. [Obsolete("Method is deprecated, please use the async version instead. Method will be removed in v2.0.")] public string ReadTextFile(string virtualPath); @@ -343,7 +359,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public Task ReadTextFileAsync(string virtualPath, CancellationToken cancellationToken = default); /// @@ -358,7 +374,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. /// Thrown if the file exists at the given path and parameter "overwrite" = false. [Obsolete("Method is deprecated, please use the async version instead. Method will be removed in v2.0.")] public void CopyFile(string virtualSourcePath, string virtualDestinationPath, bool overwrite = false); @@ -376,7 +392,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. /// Thrown if the file exists at the given path and parameter "overwrite" = false. public Task CopyFileAsync(string virtualSourcePath, string virtualDestinationPath, bool overwrite = false, CancellationToken cancellationToken = default); @@ -392,7 +408,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. /// Thrown if the file exists at the given path and parameter "overwrite" = false. [Obsolete("Method is deprecated, please use the async version instead. Method will be removed in v2.0.")] public void MoveFile(string virtualSourcePath, string virtualDestinationPath, bool overwrite = false); @@ -410,10 +426,26 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. /// Thrown if the file exists at the given path and parameter "overwrite" = false. public Task MoveFileAsync(string virtualSourcePath, string virtualDestinationPath, bool overwrite = false, CancellationToken cancellationToken = default); + /// + /// Writes stream contents to a file at the provided path. + /// + /// The virtual path (including prefix) where to write the byte array contents to. + /// The file byte array contents. + /// If a file at the destination path exists overwrite it. + /// Optional to propagate notifications that the operation should be cancelled. + /// Thrown when an exception occurs during the adapter's connection process. Contains an inner exception with more details. + /// Thrown when an exception occurs during the adapter's runtime. Contains an inner exception with more details. + /// Thrown when no adapters are registered with the file system. + /// Thrown when multiple adapters are registered with the same prefix. + /// Thrown when an adapter could not be found via the provided prefix. + /// Thrown when a prefix in the provided path could not be found. + /// Thrown if the file exists at the given path and parameter "overwrite" = false. + public Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default); + /// /// Writes byte array contents to a file at the provided path. /// @@ -478,6 +510,21 @@ public interface IFileSystem /// Thrown if the file exists at the given path and parameter "overwrite" = false. public Task WriteFileAsync(string virtualPath, string contents, bool overwrite = false, CancellationToken cancellationToken = default); + /// + /// Writes stream contents to a file at the provided path. + /// + /// The virtual path (including prefix) where to write the byte array contents to. + /// The file byte array contents. + /// Optional to propagate notifications that the operation should be cancelled. + /// Thrown when an exception occurs during the adapter's connection process. Contains an inner exception with more details. + /// Thrown when an exception occurs during the adapter's runtime. Contains an inner exception with more details. + /// Thrown when no adapters are registered with the file system. + /// Thrown when multiple adapters are registered with the same prefix. + /// Thrown when an adapter could not be found via the provided prefix. + /// Thrown when a prefix in the provided path could not be found. + /// Thrown if the file does not exists at the given path. + public Task AppendFileAsync(string virtualPath, Stream contents, CancellationToken cancellationToken = default); + /// /// Writes byte array contents to a file at the provided path. /// @@ -489,7 +536,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. [Obsolete("Method is deprecated, please use the async version instead. Method will be removed in v2.0.")] public void AppendFile(string virtualPath, byte[] contents); @@ -505,7 +552,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public Task AppendFileAsync(string virtualPath, byte[] contents, CancellationToken cancellationToken = default); /// @@ -519,7 +566,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. [Obsolete("Method is deprecated, please use the async version instead. Method will be removed in v2.0.")] public void AppendFile(string virtualPath, string contents); @@ -535,7 +582,7 @@ public interface IFileSystem /// Thrown when multiple adapters are registered with the same prefix. /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. - /// Thrown if the file does not exists at the given path. + /// Thrown if the file does not exists at the given path. public Task AppendFileAsync(string virtualPath, string contents, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/FileSystem/src/Utilities/StreamUtilities.cs b/FileSystem/src/Utilities/StreamUtilities.cs new file mode 100644 index 0000000..04b0ec4 --- /dev/null +++ b/FileSystem/src/Utilities/StreamUtilities.cs @@ -0,0 +1,19 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using SharpGrip.FileSystem.Constants; + +namespace SharpGrip.FileSystem.Utilities +{ + public static class StreamUtilities + { + public static async Task CopyContentsToMemoryStreamAsync(Stream contents, CancellationToken cancellationToken = default) + { + var memoryStream = new MemoryStream(); + contents.Seek(0, SeekOrigin.Begin); + await contents.CopyToAsync(memoryStream, AdapterConstants.DefaultMemoryStreamBufferSize, cancellationToken); + + return memoryStream; + } + } +} \ No newline at end of file