252 lines
8.6 KiB
C#
252 lines
8.6 KiB
C#
using System.Text;
|
|
using Microsoft.WindowsAzure.Storage;
|
|
using Microsoft.WindowsAzure.Storage.Blob;
|
|
using OpenHarbor.Storage.Abstractions;
|
|
|
|
namespace OpenHarbor.Storage.Azure.Blob;
|
|
|
|
public class AzureBlobStorageProvider : IStorageProvider
|
|
{
|
|
private string? _connectionString = null;
|
|
private string? _containerName = null;
|
|
|
|
public AzureBlobStorageProvider(string connectionString, string containerName)
|
|
{
|
|
SetConnectionString(connectionString);
|
|
SetContainerName(containerName);
|
|
}
|
|
|
|
public void SetContainerName(string name)
|
|
{
|
|
_containerName = name;
|
|
}
|
|
|
|
public void SetConnectionString(string connectionString)
|
|
{
|
|
_connectionString = connectionString;
|
|
}
|
|
|
|
public Task<IDirectoryInfo> CreateDirectoryAsync(string path, CancellationToken cancellationToken)
|
|
{
|
|
var ret = new AzureBlobNotExistingDirectoryInfo(path);
|
|
return Task.FromResult<IDirectoryInfo>(ret);
|
|
}
|
|
|
|
public async Task DeleteDirectoryAsync(string path, bool force = false, CancellationToken cancellationToken = default)
|
|
{
|
|
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, cancellationToken);
|
|
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, cancellationToken);
|
|
}
|
|
|
|
public Task DeleteFileAsync(string path, CancellationToken cancellationToken)
|
|
{
|
|
return GetContainer()
|
|
.GetBlobReference(path)
|
|
.DeleteIfExistsAsync(DeleteSnapshotsOption.None, null, null, null, cancellationToken);
|
|
}
|
|
|
|
public Task<bool> FileExistsAsync(string path, CancellationToken cancellationToken) =>
|
|
GetContainer()
|
|
.GetBlobReference(path)
|
|
.ExistsAsync(null, null, cancellationToken);
|
|
|
|
public async Task<List<IDirectoryInfo>> GetDirectoriesAsync(string path, CancellationToken cancellationToken) =>
|
|
(await GetListAsync(path, cancellationToken))
|
|
.Where(t => t.IsDirectory)
|
|
.Cast<IDirectoryInfo>()
|
|
.ToList();
|
|
|
|
public async Task<List<IFileInfo>> GetFilesAsync(string path, string? pattern = null, SearchOption searchOption = SearchOption.TopDirectoryOnly, CancellationToken cancellationToken = default)
|
|
{
|
|
if (pattern != null)
|
|
throw new NotSupportedException("Blob Storage does not support glob searching only prefix.");
|
|
|
|
var result = await GetListAsync(path, cancellationToken);
|
|
|
|
return result
|
|
.Where(file => false == file.IsDirectory)
|
|
.Cast<IFileInfo>()
|
|
.ToList();
|
|
}
|
|
|
|
private static string? CleanDirectoryPath(string? path)
|
|
{
|
|
if (path == null)
|
|
return null;
|
|
|
|
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, CancellationToken cancellationToken)
|
|
{
|
|
var ret = new List<IDirectoryOrFile>();
|
|
var container = GetContainer();
|
|
var finalPath = CleanDirectoryPath(path);
|
|
|
|
BlobContinuationToken? continuationToken = null;
|
|
var results = new List<IListBlobItem>();
|
|
|
|
do
|
|
{
|
|
BlobResultSegment response;
|
|
if (continuationToken == null)
|
|
response = await container.ListBlobsSegmentedAsync(finalPath, false, BlobListingDetails.None, new int?(),
|
|
continuationToken, null, null, cancellationToken);
|
|
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 Task<IFileInfo> WriteFileAsync(string sourcePath, string path, bool overrideIfExists = true, CancellationToken cancellationToken = default)
|
|
{
|
|
return WriteFileAsync(sourcePath, path, new DefaultWriteOptions
|
|
{
|
|
OverrideIfExists = overrideIfExists
|
|
}, cancellationToken);
|
|
}
|
|
|
|
public Task<IFileInfo> WriteFileAsync(byte[] bytes, string path, bool overrideIfExists = true, CancellationToken cancellationToken = default)
|
|
{
|
|
return WriteFileAsync(bytes, path, new DefaultWriteOptions
|
|
{
|
|
OverrideIfExists = overrideIfExists
|
|
}, cancellationToken);
|
|
}
|
|
|
|
public Task<IFileInfo> WriteFileAsync(Stream stream, string path, bool overrideIfExists = true, CancellationToken cancellationToken = default)
|
|
{
|
|
return WriteFileAsync(stream, path, new DefaultWriteOptions
|
|
{
|
|
OverrideIfExists = overrideIfExists
|
|
}, cancellationToken);
|
|
}
|
|
|
|
public async Task<Stream> GetFileStreamAsync(string path, CancellationToken cancellationToken)
|
|
{
|
|
await ThrowNotExistingAsync(path, cancellationToken);
|
|
var container = GetContainer();
|
|
var blob = container.GetBlockBlobReference(path);
|
|
return await blob.OpenReadAsync(null, null, null, cancellationToken);
|
|
}
|
|
|
|
private async Task ThrowNotExistingAsync(string path, CancellationToken cancellationToken)
|
|
{
|
|
if (false == await FileExistsAsync(path, cancellationToken))
|
|
throw new FileDoesNotExistException(path);
|
|
}
|
|
|
|
public async Task<byte[]> GetFileBytesAsync(string path, CancellationToken cancellationToken)
|
|
{
|
|
await ThrowNotExistingAsync(path, cancellationToken);
|
|
var container = GetContainer();
|
|
var blob = container.GetBlockBlobReference(path);
|
|
var bytes = new byte[blob.Properties.Length];
|
|
await blob.DownloadToByteArrayAsync(bytes, 0, null, null, null, cancellationToken);
|
|
return bytes;
|
|
}
|
|
|
|
public async Task<string> GetFileContentAsync(string path, Encoding encoding, CancellationToken cancellationToken)
|
|
{
|
|
await ThrowNotExistingAsync(path, cancellationToken);
|
|
return encoding.GetString(await GetFileBytesAsync(path, cancellationToken));
|
|
}
|
|
|
|
public bool IsFileNameAllowed(string fileName) => true;
|
|
|
|
public string SanitizeFileName(string key, string replacement) => key;
|
|
|
|
public async Task<IFileInfo> WriteFileAsync(string sourcePath, string path, IWriteFileOptions options, CancellationToken cancellationToken)
|
|
{
|
|
if (options is null)
|
|
throw new ArgumentNullException(nameof(options));
|
|
|
|
if (!options.OverrideIfExists && await FileExistsAsync(path, cancellationToken))
|
|
throw new FileAlreadyExistsException(path);
|
|
|
|
var container = GetContainer();
|
|
var blob = container.GetBlockBlobReference(path);
|
|
await blob.UploadFromFileAsync(sourcePath, null, null, null, cancellationToken);
|
|
return new AzureBlobFileInfo(blob);
|
|
}
|
|
|
|
public async Task<IFileInfo> WriteFileAsync(byte[] bytes, string path, IWriteFileOptions options, CancellationToken cancellationToken)
|
|
{
|
|
if (options is null)
|
|
throw new ArgumentNullException(nameof(options));
|
|
|
|
if (!options.OverrideIfExists && await FileExistsAsync(path, cancellationToken))
|
|
throw new FileAlreadyExistsException(path);
|
|
|
|
var container = GetContainer();
|
|
var blob = container.GetBlockBlobReference(path);
|
|
|
|
await blob.UploadFromByteArrayAsync(bytes, 0, bytes.Length, null, null, null, cancellationToken);
|
|
return new AzureBlobFileInfo(blob);
|
|
}
|
|
|
|
public async Task<IFileInfo> WriteFileAsync(Stream stream, string path, IWriteFileOptions options, CancellationToken cancellationToken)
|
|
{
|
|
if (options is null)
|
|
throw new ArgumentNullException(nameof(options));
|
|
|
|
if (!options.OverrideIfExists && await FileExistsAsync(path, cancellationToken))
|
|
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, null, null, null, cancellationToken);
|
|
return new AzureBlobFileInfo(blob);
|
|
}
|
|
}
|