Add project files.

This commit is contained in:
David Lebee 2020-03-13 11:18:25 -04:00
parent 3a7d5f7e02
commit e88e9872d2
20 changed files with 943 additions and 0 deletions

View File

@ -0,0 +1,29 @@
using Microsoft.WindowsAzure.Storage.Blob;
using PoweredSoft.Storage.Core;
namespace PoweredSoft.Storage.Azure.Blob
{
public class AzureBlobDirectoryInfo : IDirectoryInfo
{
private CloudBlobDirectory blobDirectory;
public AzureBlobDirectoryInfo(CloudBlobDirectory blobDirectory)
{
this.blobDirectory = blobDirectory;
}
public string Path => blobDirectory.Prefix.TrimEnd('/');
public bool IsDirectory => true;
}
public class AzureBlobNotExistingDirectoryInfo : IDirectoryInfo
{
public AzureBlobNotExistingDirectoryInfo(string path)
{
Path = path;
}
public string Path { get; }
public bool IsDirectory => true;
}
}

View File

@ -0,0 +1,30 @@
using Microsoft.WindowsAzure.Storage.Blob;
using PoweredSoft.Storage.Core;
using System;
using System.Collections.Generic;
using System.Text;
namespace PoweredSoft.Storage.Azure.Blob
{
public class AzureBlobFileInfo : IFileInfo
{
private readonly CloudBlockBlob fileBlock;
public AzureBlobFileInfo(CloudBlockBlob fileBlock)
{
this.fileBlock = fileBlock;
}
public string FileName => System.IO.Path.GetFileName(fileBlock.Name);
public string Extension => System.IO.Path.GetExtension(fileBlock.Name);
public long FileSize => fileBlock.Properties.Length;
public DateTimeOffset? CreatedTime => fileBlock.Properties.Created;
public DateTimeOffset? LastModifiedTime => fileBlock.Properties.LastModified;
public DateTimeOffset? LastAccessTime => null;
public DateTime? CreatedTimeUtc => CreatedTime?.UtcDateTime;
public DateTime? LastModifiedTimeUtc => LastModifiedTime?.UtcDateTime;
public DateTime? LastAccessTimeUtc => null;
public string Path => fileBlock.Uri.LocalPath.Replace($"/{fileBlock.Container.Name}/", "");
public bool IsDirectory => false;
}
}

View File

@ -0,0 +1,216 @@
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using PoweredSoft.Storage.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PoweredSoft.Storage.Azure.Blob
{
public class AzureBlobStorageProvider : IStorageProvider
{
private string connectionString = null;
private string containerName = null;
public AzureBlobStorageProvider()
{
}
public AzureBlobStorageProvider(string connectionString, string containerName)
{
this.SetConnectionString(connectionString);
this.SetContainerName(containerName);
}
public void SetContainerName(string name)
{
this.containerName = name;
}
public void SetConnectionString(string connectionString)
{
this.connectionString = connectionString;
}
public Task<IDirectoryInfo> CreateDirectoryAsync(string path)
{
var ret = new AzureBlobNotExistingDirectoryInfo(path);
return Task.FromResult<IDirectoryInfo>(ret);
}
public async Task DeleteDirectoryAsync(string path, bool force = false)
{
var ret = new List<IDirectoryOrFile>();
var container = GetContainer();
var finalPath = CleanDirectoryPath(path);
BlobContinuationToken continuationToken = null;
List<IListBlobItem> results = new List<IListBlobItem>();
do
{
BlobResultSegment response;
if (continuationToken == null)
response = await container.ListBlobsSegmentedAsync(finalPath, true, BlobListingDetails.All, null, continuationToken, null, null);
else
response = await container.ListBlobsSegmentedAsync(continuationToken);
continuationToken = response.ContinuationToken;
results.AddRange(response.Results);
}
while (continuationToken != null);
var files = results.Where(t => t is CloudBlockBlob).Cast<CloudBlockBlob>().ToList();
foreach (var file in files)
await this.DeleteFileAsync(file.Name);
}
public Task DeleteFileAsync(string path)
{
return GetContainer().GetBlobReference(path).DeleteIfExistsAsync();
}
public Task<bool> FileExistsAsync(string path)
{
return GetContainer().GetBlobReference(path).ExistsAsync();
}
public async Task<List<IDirectoryInfo>> GetDirectories(string path)
{
return (await this.GetListAsync(path)).Where(t => t.IsDirectory).Cast<IDirectoryInfo>().ToList();
}
public async Task<List<IFileInfo>> GetFilesAsync(string path, string pattern = null, SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
if (pattern != null)
throw new NotSupportedException("Blob Storage does not support glob searching only prefix.");
var result = await GetListAsync(path);
var finalResult = result.Where(t => !t.IsDirectory).Cast<IFileInfo>().ToList();
return finalResult;
}
private string CleanDirectoryPath(string path)
{
if (path == null)
return path;
path = path.TrimEnd('/');
if (path != "")
path += "/";
return path;
}
private CloudBlobContainer GetContainer()
{
var account = CloudStorageAccount.Parse(connectionString);
var client = account.CreateCloudBlobClient();
var container = client.GetContainerReference(containerName);
return container;
}
public async Task<List<IDirectoryOrFile>> GetListAsync(string path)
{
var ret = new List<IDirectoryOrFile>();
var container = GetContainer();
var finalPath = CleanDirectoryPath(path);
BlobContinuationToken continuationToken = null;
List<IListBlobItem> results = new List<IListBlobItem>();
do
{
BlobResultSegment response;
if (continuationToken == null)
response = await container.ListBlobsSegmentedAsync(finalPath, continuationToken);
else
response = await container.ListBlobsSegmentedAsync(continuationToken);
continuationToken = response.ContinuationToken;
results.AddRange(response.Results);
}
while (continuationToken != null);
foreach (var result in results)
{
if (result is CloudBlobDirectory blobDirectory)
ret.Add(new AzureBlobDirectoryInfo(blobDirectory));
else if (result is CloudBlockBlob blobBlock)
ret.Add(new AzureBlobFileInfo(blobBlock));
}
return ret;
}
public async Task<IFileInfo> WriteFileAsync(string sourcePath, string path, bool overrideIfExists = true)
{
if (!overrideIfExists && await FileExistsAsync(path))
throw new FileAlreadyExistsException(path);
var container = GetContainer();
var blob = container.GetBlockBlobReference(path);
await blob.UploadFromFileAsync(sourcePath);
return new AzureBlobFileInfo(blob);
}
public async Task<IFileInfo> WriteFileAsync(byte[] bytes, string path, bool overrideIfExists = true)
{
if (!overrideIfExists && await FileExistsAsync(path))
throw new FileAlreadyExistsException(path);
var container = GetContainer();
var blob = container.GetBlockBlobReference(path);
await blob.UploadFromByteArrayAsync(bytes, 0, bytes.Length);
return new AzureBlobFileInfo(blob);
}
public async Task<IFileInfo> WriteFileAsync(Stream stream, string path, bool overrideIfExists = true)
{
if (!overrideIfExists && await FileExistsAsync(path))
throw new FileAlreadyExistsException(path);
if (stream.CanSeek && stream.Position != 0)
stream.Seek(0, SeekOrigin.Begin);
var container = GetContainer();
var blob = container.GetBlockBlobReference(path);
await blob.UploadFromStreamAsync(stream);
return new AzureBlobFileInfo(blob);
}
public async Task<Stream> GetFileStreamAsync(string path)
{
await ThrowNotExistingAsync(path);
var container = GetContainer();
var blob = container.GetBlockBlobReference(path);
return await blob.OpenReadAsync();
}
private async Task ThrowNotExistingAsync(string path)
{
if (false == await this.FileExistsAsync(path))
throw new FileDoesNotExistException(path);
}
public async Task<byte[]> GetFileBytesAsync(string path)
{
await ThrowNotExistingAsync(path);
var container = GetContainer();
var blob = container.GetBlockBlobReference(path);
var bytes = new byte[blob.Properties.Length];
await blob.DownloadToByteArrayAsync(bytes, 0);
return bytes;
}
public async Task<string> GetFileContentAsync(string path, Encoding encoding)
{
await ThrowNotExistingAsync(path);
var container = GetContainer();
return encoding.GetString(await this.GetFileBytesAsync(path));
}
}
}

View File

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Copyright>Powered Softwares Inc.</Copyright>
<Version>1.1.0$(VersionSuffix)</Version>
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;amp;r=g&amp;amp;d=retro</PackageIconUrl>
<Product>PoweredSoft.Storage.Azure</Product>
<Description>initial</Description>
<PackageId>PoweredSoft.Storage.Azure</PackageId>
<PackageReleaseNotes>initial</PackageReleaseNotes>
<PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
<Company>PoweredSoft</Company>
<Authors>PoweredSoft</Authors>
<Deterministic>False</Deterministic>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="WindowsAzure.Storage" Version="9.3.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PoweredSoft.Storage.Core\PoweredSoft.Storage.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,12 @@
using System;
using System.Runtime.Serialization;
namespace PoweredSoft.Storage.Core
{
public class FileAlreadyExistsException : Exception
{
public FileAlreadyExistsException(string path) : base($"{path} already exists..")
{
}
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace PoweredSoft.Storage.Core
{
public class FileDoesNotExistException : Exception
{
public FileDoesNotExistException(string path) : base($"{path} does not exist.")
{
}
}
}

View File

@ -0,0 +1,7 @@
namespace PoweredSoft.Storage.Core
{
public interface IDirectoryInfo : IDirectoryOrFile
{
}
}

View File

@ -0,0 +1,8 @@
namespace PoweredSoft.Storage.Core
{
public interface IDirectoryOrFile
{
string Path { get; }
bool IsDirectory { get; }
}
}

View File

@ -0,0 +1,17 @@
using System;
namespace PoweredSoft.Storage.Core
{
public interface IFileInfo : IDirectoryOrFile
{
string FileName { get; }
string Extension { get; }
long FileSize { get; }
DateTimeOffset? CreatedTime { get; }
DateTimeOffset? LastModifiedTime { get; }
DateTimeOffset? LastAccessTime { get; }
DateTime? CreatedTimeUtc { get; }
DateTime? LastModifiedTimeUtc { get; }
DateTime? LastAccessTimeUtc { get; }
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace PoweredSoft.Storage.Core
{
public interface IStorageProvider
{
Task<List<IDirectoryOrFile>> GetListAsync(string path);
Task<List<IDirectoryInfo>> GetDirectories(string path);
Task<List<IFileInfo>> GetFilesAsync(string path, string pattern = null, SearchOption searchOption = SearchOption.TopDirectoryOnly);
Task<IFileInfo> WriteFileAsync(string sourcePath, string path, bool overrideIfExists = true);
Task<IFileInfo> WriteFileAsync(byte[] bytes, string path, bool overrideIfExists = true);
Task<IFileInfo> WriteFileAsync(Stream stream, string path, bool overrideIfExists = true);
Task<Stream> GetFileStreamAsync(string path);
Task<byte[]> GetFileBytesAsync(string path);
Task<string> GetFileContentAsync(string path, Encoding encoding);
Task<bool> FileExistsAsync(string path);
Task DeleteFileAsync(string path);
Task DeleteDirectoryAsync(string path, bool force = false);
Task<IDirectoryInfo> CreateDirectoryAsync(string path);
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Copyright>Powered Softwares Inc.</Copyright>
<Version>1.1.0$(VersionSuffix)</Version>
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;amp;r=g&amp;amp;d=retro</PackageIconUrl>
<Product>PoweredSoft.Storage.Core</Product>
<Description>initial</Description>
<PackageId>PoweredSoft.Storage.Core</PackageId>
<PackageReleaseNotes>initial</PackageReleaseNotes>
<PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
<Company>PoweredSoft</Company>
<Authors>PoweredSoft</Authors>
<Deterministic>False</Deterministic>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,15 @@
using PoweredSoft.Storage.Core;
namespace PoweredSoft.Storage.Physical
{
public class PhysicalDirectoryInfo : IDirectoryInfo
{
public PhysicalDirectoryInfo(string path)
{
Path = path;
}
public string Path { get; }
public bool IsDirectory => true;
}
}

View File

@ -0,0 +1,28 @@
using PoweredSoft.Storage.Core;
using System;
using System.IO;
namespace PoweredSoft.Storage.Physical
{
public class PhysicalFileInfo : IFileInfo
{
private readonly FileInfo fileInfo;
public PhysicalFileInfo(FileInfo fileInfo)
{
this.fileInfo = fileInfo;
}
public string Path => fileInfo.FullName;
public string FileName => fileInfo.Name;
public string Extension => fileInfo.Extension;
public long FileSize => fileInfo.Length;
public DateTimeOffset? CreatedTime => fileInfo.CreationTime;
public DateTimeOffset? LastModifiedTime => fileInfo.LastWriteTime;
public DateTimeOffset? LastAccessTime => fileInfo.LastAccessTime;
public DateTime? CreatedTimeUtc => fileInfo.CreationTimeUtc;
public DateTime? LastModifiedTimeUtc => fileInfo.LastWriteTimeUtc;
public DateTime? LastAccessTimeUtc => fileInfo.LastAccessTimeUtc;
public bool IsDirectory => false;
}
}

View File

@ -0,0 +1,149 @@
using PoweredSoft.Storage.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PoweredSoft.Storage.Physical
{
public class PhysicalStorageProvider : IStorageProvider
{
public Task<IDirectoryInfo> CreateDirectoryAsync(string path)
{
var directoryInfo = System.IO.Directory.CreateDirectory(path);
var result = new PhysicalDirectoryInfo(path);
return Task.FromResult<IDirectoryInfo>(result);
}
public Task DeleteDirectoryAsync(string path, bool force = false)
{
if (force)
Directory.Delete(path, true);
else
Directory.Delete(path);
return Task.CompletedTask;
}
public Task DeleteFileAsync(string path)
{
System.IO.File.Delete(path);
return Task.CompletedTask;
}
public Task<bool> FileExistsAsync(string path)
{
return Task.FromResult(File.Exists(path));
}
public Task<List<IDirectoryInfo>> GetDirectories(string path)
{
var directoryInfo = new DirectoryInfo(path);
var directories = directoryInfo.GetDirectories();
var directoriesConverted = directories.Select(t => new PhysicalDirectoryInfo(t.FullName)).AsEnumerable<IDirectoryInfo>().ToList();
return Task.FromResult(directoriesConverted);
}
public async Task<byte[]> GetFileBytesAsync(string path)
{
await ThrowNotExistingAsync(path);
return File.ReadAllBytes(path);
}
public async Task<string> GetFileContentAsync(string path, Encoding encoding)
{
await ThrowNotExistingAsync(path);
return File.ReadAllText(path, encoding);
}
public Task<List<IFileInfo>> GetFilesAsync(string path, string pattern = null, SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
var directoryInfo = new DirectoryInfo(path);
FileInfo[] files;
if (string.IsNullOrWhiteSpace(pattern))
files = directoryInfo.GetFiles();
else
files = directoryInfo.GetFiles(pattern, searchOption);
var result = files.Select(fileInfo => new PhysicalFileInfo(fileInfo)).AsEnumerable<IFileInfo>().ToList();
return Task.FromResult(result);
}
private async Task ThrowNotExistingAsync(string path)
{
if (false == await this.FileExistsAsync(path))
throw new FileDoesNotExistException(path);
}
public async Task<Stream> GetFileStreamAsync(string path)
{
await ThrowNotExistingAsync(path);
return new FileStream(path, FileMode.Open, FileAccess.Read);
}
public async Task<List<IDirectoryOrFile>> GetListAsync(string path)
{
var files = await this.GetFilesAsync(path);
var directories = await this.GetDirectories(path);
var result = files.AsEnumerable<IDirectoryOrFile>().Concat(directories.AsEnumerable<IDirectoryOrFile>()).ToList();
return result;
}
public async Task<IFileInfo> WriteFileAsync(string sourcePath, string path, bool overrideIfExists = true)
{
if (!overrideIfExists && await FileExistsAsync(path))
throw new FileAlreadyExistsException(path);
CreateDirectoryIfNotExisting(path);
System.IO.File.Copy(sourcePath, path, overrideIfExists);
var fileInfo = new FileInfo(path);
var ret = new PhysicalFileInfo(fileInfo);
return ret;
}
public async Task<IFileInfo> WriteFileAsync(byte[] bytes, string path, bool overrideIfExists = true)
{
if (!overrideIfExists && await FileExistsAsync(path))
throw new FileAlreadyExistsException(path);
CreateDirectoryIfNotExisting(path);
File.WriteAllBytes(path, bytes);
var fileInfo = new FileInfo(path);
var physicalinfo = new PhysicalFileInfo(fileInfo);
return physicalinfo;
}
public async Task<IFileInfo> WriteFileAsync(Stream stream, string path, bool overrideIfExists = true)
{
if (!overrideIfExists && await FileExistsAsync(path))
throw new FileAlreadyExistsException(path);
CreateDirectoryIfNotExisting(path);
if (stream.CanSeek && stream.Position != 0)
stream.Seek(0, SeekOrigin.Begin);
using (var fileStream = new FileStream(path, FileMode.CreateNew, FileAccess.Write))
{
await stream.CopyToAsync(fileStream);
fileStream.Close();
}
var fileInfo = new FileInfo(path);
var physicalinfo = new PhysicalFileInfo(fileInfo);
return physicalinfo;
}
private void CreateDirectoryIfNotExisting(string path)
{
var directoryPath = Path.GetDirectoryName(path);
if (!Directory.Exists(directoryPath))
Directory.CreateDirectory(directoryPath);
}
}
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Copyright>Powered Softwares Inc.</Copyright>
<Version>1.1.0$(VersionSuffix)</Version>
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;amp;r=g&amp;amp;d=retro</PackageIconUrl>
<Product>PoweredSoft.Storage.Physical</Product>
<Description>initial</Description>
<PackageId>PoweredSoft.Storage.Physical</PackageId>
<PackageReleaseNotes>initial</PackageReleaseNotes>
<PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
<Company>PoweredSoft</Company>
<Authors>PoweredSoft</Authors>
<Deterministic>False</Deterministic>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PoweredSoft.Storage.Core\PoweredSoft.Storage.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<Copyright>Powered Softwares Inc.</Copyright>
<Version>1.1.0$(VersionSuffix)</Version>
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;amp;r=g&amp;amp;d=retro</PackageIconUrl>
<Product>PoweredSoft.Storage.S3</Product>
<Description>initial</Description>
<PackageId>PoweredSoft.Storage.S3</PackageId>
<PackageReleaseNotes>initial</PackageReleaseNotes>
<PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
<Company>PoweredSoft</Company>
<Authors>PoweredSoft</Authors>
<Deterministic>False</Deterministic>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PoweredSoft.Storage.Core\PoweredSoft.Storage.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="3.3.110.10" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,33 @@
using System;
using Amazon.S3.Model;
using PoweredSoft.Storage.Core;
namespace PoweredSoft.Storage.S3
{
public class S3FileInfo : IFileInfo
{
public S3FileInfo(S3Object file)
{
Path = file.Key;
CreatedTime = file.LastModified;
LastModifiedTime = file.LastModified;
LastAccessTime = file.LastModified;
CreatedTimeUtc = file.LastModified.ToUniversalTime();
LastAccessTimeUtc = file.LastModified.ToUniversalTime();
LastModifiedTimeUtc = file.LastModified.ToUniversalTime();
FileSize = file.Size;
}
public string FileName => System.IO.Path.GetFileName(Path);
public string Extension => System.IO.Path.GetExtension(Path);
public long FileSize { get; }
public DateTimeOffset? CreatedTime { get; }
public DateTimeOffset? LastModifiedTime { get; }
public DateTimeOffset? LastAccessTime { get; }
public DateTime? CreatedTimeUtc { get; }
public DateTime? LastModifiedTimeUtc { get; }
public DateTime? LastAccessTimeUtc { get; }
public string Path { get; }
public bool IsDirectory => false;
}
}

View File

@ -0,0 +1,15 @@
using PoweredSoft.Storage.Core;
namespace PoweredSoft.Storage.S3
{
public class S3NotExistingDirectoryInfo : IDirectoryInfo
{
public S3NotExistingDirectoryInfo(string path)
{
Path = path;
}
public string Path { get; }
public bool IsDirectory => true;
}
}

View File

@ -0,0 +1,211 @@
using Amazon.S3;
using Amazon.S3.Model;
using PoweredSoft.Storage.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PoweredSoft.Storage.S3
{
public class S3StorageProvider : IStorageProvider
{
private readonly string endpoint;
private readonly string bucketName;
private readonly string accessKey;
private readonly string secret;
public S3StorageProvider(string endpoint, string bucketName, string accessKey, string secret)
{
this.endpoint = endpoint;
this.bucketName = bucketName;
this.accessKey = accessKey;
this.secret = secret;
}
private IAmazonS3 GetClient()
{
var config = new AmazonS3Config
{
ServiceURL = $"https://{endpoint}"
};
var client = new AmazonS3Client(this.accessKey, this.secret, config);
return client;
}
public Task<IDirectoryInfo> CreateDirectoryAsync(string path)
{
return Task.FromResult<IDirectoryInfo>(new S3NotExistingDirectoryInfo(path));
}
/// <summary>
/// Can only delete 1000 at a time.
/// </summary>
/// <param name="path"></param>
/// <param name="force"></param>
/// <returns></returns>
public async Task DeleteDirectoryAsync(string path, bool force = false)
{
using var client = GetClient();
var files = await this.GetS3FilesAsync(prefix: path, delimiter: null);
var next = files.AsQueryable();
while(next.Any())
{
var next1000 = next.Take(1000);
var keys = next1000.Select(s3o => new KeyVersion { Key = s3o.Key }).ToList();
await client.DeleteObjectsAsync(new DeleteObjectsRequest
{
BucketName = this.bucketName,
Objects = keys
});
next = next.Skip(1000);
}
}
public async Task DeleteFileAsync(string path)
{
using var client = GetClient();
var response = await client.DeleteObjectAsync(new DeleteObjectRequest
{
BucketName = this.bucketName,
Key = path
});
}
public async Task<bool> FileExistsAsync(string path)
{
var item = await GetS3FileByPath(path);
return item != null;
}
public Task<List<IDirectoryInfo>> GetDirectories(string path)
{
return Task.FromResult(new List<IDirectoryInfo>());
}
public async Task<byte[]> GetFileBytesAsync(string path)
{
using var fileStream = await this.GetFileStreamAsync(path);
using var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream);
return memoryStream.ToArray();
}
public async Task<string> GetFileContentAsync(string path, Encoding encoding)
{
using var fileStream = await this.GetFileStreamAsync(path);
using var streamReader = new StreamReader(fileStream, encoding);
return await streamReader.ReadToEndAsync();
}
public async Task<List<IFileInfo>> GetFilesAsync(string path, string pattern = null, SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
if (pattern != null)
throw new NotSupportedException();
string finalPath = SantizeDirectoryRequest(path);
var s3Files = await this.GetS3FilesAsync(prefix: finalPath, delimiter: "/");
var ret = s3Files.Select(s3 => new S3FileInfo(s3)).AsEnumerable<IFileInfo>().ToList();
return ret;
}
private static string SantizeDirectoryRequest(string path)
{
string finalPath;
if (path == "/")
finalPath = "";
else
finalPath = $"{path?.TrimEnd('/')}/";
return finalPath;
}
public Task<Stream> GetFileStreamAsync(string path)
{
using var client = GetClient();
return client.GetObjectStreamAsync(this.bucketName, path, null);
}
protected virtual async Task<IEnumerable<S3Object>> GetS3FilesAsync(string prefix = null, string delimiter = null)
{
using var client = GetClient();
var items = new List<S3Object>();
string nextKey = null;
do
{
var response = await client.ListObjectsV2Async(new ListObjectsV2Request
{
BucketName = this.bucketName,
Prefix = prefix,
Delimiter = delimiter,
MaxKeys = 1000,
ContinuationToken = nextKey
});
items.AddRange(response.S3Objects);
nextKey = response.NextContinuationToken;
} while (nextKey != null);
return items;
}
public async Task<List<IDirectoryOrFile>> GetListAsync(string path)
{
var files = await this.GetFilesAsync(path);
return files.Cast<IDirectoryOrFile>().ToList();
}
public async Task<IFileInfo> WriteFileAsync(string sourcePath, string path, bool overrideIfExists = true)
{
using var client = GetClient();
await client.UploadObjectFromFilePathAsync(this.bucketName, path, sourcePath, null);
var file = await GetFileInfoByPath(path);
return file;
}
public Task<IFileInfo> WriteFileAsync(byte[] bytes, string path, bool overrideIfExists = true)
{
return WriteFileAsync(new MemoryStream(bytes), path, overrideIfExists: overrideIfExists);
}
public async Task<IFileInfo> WriteFileAsync(Stream stream, string path, bool overrideIfExists = true)
{
if (!overrideIfExists && await FileExistsAsync(path))
throw new FileAlreadyExistsException(path);
using var client = GetClient();
var request = new PutObjectRequest
{
BucketName = this.bucketName,
InputStream = stream,
Key = path
};
var result = await client.PutObjectAsync(request);
var file = await GetFileInfoByPath(path);
return file;
}
private async Task<S3Object> GetS3FileByPath(string path)
{
var files = await this.GetS3FilesAsync(path);
var s3o = files.FirstOrDefault();
return s3o;
}
private async Task<IFileInfo> GetFileInfoByPath(string path)
{
var s3o = await GetS3FileByPath(path);
if (s3o == null)
throw new FileDoesNotExistException(path);
var ret = new S3FileInfo(s3o);
return ret;
}
}
}

43
PoweredSoft.Storage.sln Normal file
View File

@ -0,0 +1,43 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.852
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.Storage.Azure", "PoweredSoft.Storage.Azure\PoweredSoft.Storage.Azure.csproj", "{B937F389-07BE-4235-B2A8-7D1229B3D0FC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.Storage.Core", "PoweredSoft.Storage.Core\PoweredSoft.Storage.Core.csproj", "{C9CBCC98-B38E-4949-AF7C-BD291E09A1F4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.Storage.Physical", "PoweredSoft.Storage.Physical\PoweredSoft.Storage.Physical.csproj", "{349E6B89-BEBB-4883-95C8-9E28F9FEF24C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.Storage.S3", "PoweredSoft.Storage.S3\PoweredSoft.Storage.S3.csproj", "{457912EA-48E3-4B2E-941F-2116D18C6D88}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B937F389-07BE-4235-B2A8-7D1229B3D0FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B937F389-07BE-4235-B2A8-7D1229B3D0FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B937F389-07BE-4235-B2A8-7D1229B3D0FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B937F389-07BE-4235-B2A8-7D1229B3D0FC}.Release|Any CPU.Build.0 = Release|Any CPU
{C9CBCC98-B38E-4949-AF7C-BD291E09A1F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9CBCC98-B38E-4949-AF7C-BD291E09A1F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9CBCC98-B38E-4949-AF7C-BD291E09A1F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9CBCC98-B38E-4949-AF7C-BD291E09A1F4}.Release|Any CPU.Build.0 = Release|Any CPU
{349E6B89-BEBB-4883-95C8-9E28F9FEF24C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{349E6B89-BEBB-4883-95C8-9E28F9FEF24C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{349E6B89-BEBB-4883-95C8-9E28F9FEF24C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{349E6B89-BEBB-4883-95C8-9E28F9FEF24C}.Release|Any CPU.Build.0 = Release|Any CPU
{457912EA-48E3-4B2E-941F-2116D18C6D88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{457912EA-48E3-4B2E-941F-2116D18C6D88}.Debug|Any CPU.Build.0 = Debug|Any CPU
{457912EA-48E3-4B2E-941F-2116D18C6D88}.Release|Any CPU.ActiveCfg = Release|Any CPU
{457912EA-48E3-4B2E-941F-2116D18C6D88}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CD56AA1A-2EE6-4ED2-853A-82C696A975AA}
EndGlobalSection
EndGlobal