mirror of https://github.com/Squidex/squidex.git
11 changed files with 107 additions and 209 deletions
@ -0,0 +1,90 @@ |
|||
// ==========================================================================
|
|||
// AzureBlobAssetStore.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.WindowsAzure.Storage.Blob; |
|||
using Squidex.Infrastructure.Assets; |
|||
|
|||
namespace Squidex.Infrastructure.Azure.Storage |
|||
{ |
|||
public class AzureBlobAssetStore : IAssetStore, IExternalSystem |
|||
{ |
|||
private readonly IStorageAccountManager azureStorageAccount; |
|||
private readonly string containerName; |
|||
private CloudBlobContainer blobContainer; |
|||
private const string AssetVersion = "AssetVersion"; |
|||
private const string AssetId = "AssetId"; |
|||
|
|||
public AzureBlobAssetStore(IStorageAccountManager azureStorageAccount, string containerName) |
|||
{ |
|||
Guard.NotNullOrEmpty(containerName, nameof(containerName)); |
|||
Guard.NotNull(azureStorageAccount, nameof(azureStorageAccount)); |
|||
|
|||
this.azureStorageAccount = azureStorageAccount; |
|||
this.containerName = containerName; |
|||
} |
|||
|
|||
public async Task DownloadAsync(string id, long version, string suffix, Stream stream) |
|||
{ |
|||
var blobName = GetObjectName(id, version, suffix); |
|||
var blob = blobContainer.GetBlockBlobReference(blobName); |
|||
|
|||
if (!await blob.ExistsAsync()) |
|||
return; |
|||
|
|||
await blob.DownloadToStreamAsync(stream); |
|||
} |
|||
|
|||
public async Task UploadAsync(string id, long version, string suffix, Stream stream) |
|||
{ |
|||
var blobName = GetObjectName(id, version, suffix); |
|||
var blob = blobContainer.GetBlockBlobReference(blobName); |
|||
|
|||
if (await blob.ExistsAsync()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (!blob.Metadata.ContainsKey(AssetVersion)) |
|||
blob.Metadata.Add(AssetVersion, version.ToString()); |
|||
else |
|||
blob.Metadata[AssetVersion] = version.ToString(); |
|||
|
|||
blob.Metadata[AssetId] = id; |
|||
|
|||
await blob.UploadFromStreamAsync(stream); |
|||
await blob.SetMetadataAsync(); |
|||
} |
|||
|
|||
public void Connect() |
|||
{ |
|||
blobContainer = azureStorageAccount.GetContainer(containerName); |
|||
} |
|||
|
|||
private string GetObjectName(string id, long version, string suffix) |
|||
{ |
|||
Guard.NotNullOrEmpty(id, nameof(id)); |
|||
|
|||
if (blobContainer == null) |
|||
{ |
|||
throw new InvalidOperationException("No connection established yet."); |
|||
} |
|||
|
|||
var name = $"{id}_{version}"; |
|||
|
|||
if (!string.IsNullOrWhiteSpace(suffix)) |
|||
{ |
|||
name += "_" + suffix; |
|||
} |
|||
|
|||
return name; |
|||
} |
|||
} |
|||
} |
|||
@ -1,147 +0,0 @@ |
|||
// ==========================================================================
|
|||
// AzureBlobAssetStore.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.WindowsAzure.Storage; |
|||
using Microsoft.WindowsAzure.Storage.Blob; |
|||
using Squidex.Infrastructure.Assets; |
|||
|
|||
namespace Squidex.Infrastructure.AzureStorage |
|||
{ |
|||
public class AzureBlobAssetStore : IAssetStore, IExternalSystem |
|||
{ |
|||
private readonly IBlobContainerProvider blobContainerProvider; |
|||
private readonly string containerName; |
|||
private CloudBlobContainer blobContainer; |
|||
private const string AssetVersion = "AssetVersion"; |
|||
private const string AssetId = "AssetId"; |
|||
|
|||
public AzureBlobAssetStore(IBlobContainerProvider blobContainerProvider, string containerName) |
|||
{ |
|||
Guard.NotNullOrEmpty(containerName, nameof(containerName)); |
|||
Guard.NotNull(blobContainerProvider, nameof(blobContainerProvider)); |
|||
|
|||
this.blobContainerProvider = blobContainerProvider; |
|||
this.containerName = containerName; |
|||
} |
|||
|
|||
public async Task DownloadAsync(string id, long version, string suffix, Stream stream) |
|||
{ |
|||
var blobName = GetObjectName(id, suffix); |
|||
var blob = blobContainer.GetBlockBlobReference(blobName); |
|||
|
|||
if (!await blob.ExistsAsync()) |
|||
return; |
|||
|
|||
// look for the requested version
|
|||
// first check if the original blob has the requested version
|
|||
if (blob.Metadata.TryGetValue(AssetVersion, out string verionStr)) |
|||
{ |
|||
if (long.TryParse(verionStr, out long blobVersion)) |
|||
{ |
|||
// if not, then look for that snapshot which has the requested version number.
|
|||
if (blobVersion != version) |
|||
{ |
|||
var snapshotBlob = await FindSnapshotAsync(id, version); |
|||
blob = snapshotBlob ?? blob; |
|||
} |
|||
} |
|||
} |
|||
|
|||
await blob.DownloadToStreamAsync(stream); |
|||
} |
|||
|
|||
public async Task UploadAsync(string id, long version, string suffix, Stream stream) |
|||
{ |
|||
var blobName = GetObjectName(id, suffix); |
|||
var blob = blobContainer.GetBlockBlobReference(blobName); |
|||
|
|||
if (await blob.ExistsAsync()) |
|||
{ |
|||
// if it's already exist create a snapshot, and we overwrite the source blob of the snapshot.
|
|||
// NOTE: not sure if there is an
|
|||
await CreateVersioningSnapshotAsync(blob, id, version); |
|||
} |
|||
|
|||
if (!blob.Metadata.ContainsKey(AssetVersion)) |
|||
blob.Metadata.Add(AssetVersion, version.ToString()); |
|||
else |
|||
blob.Metadata[AssetVersion] = version.ToString(); |
|||
|
|||
blob.Metadata[AssetId] = id; |
|||
|
|||
await blob.UploadFromStreamAsync(stream); |
|||
await blob.SetMetadataAsync(); |
|||
} |
|||
|
|||
public async void Connect() |
|||
{ |
|||
blobContainer = await blobContainerProvider.GetContainerAsync(containerName); |
|||
} |
|||
|
|||
private string GetObjectName(string id, string suffix) |
|||
{ |
|||
Guard.NotNullOrEmpty(id, nameof(id)); |
|||
|
|||
if (blobContainer == null) |
|||
{ |
|||
throw new InvalidOperationException("No connection established yet."); |
|||
} |
|||
|
|||
var name = $"{id}"; |
|||
|
|||
if (!string.IsNullOrWhiteSpace(suffix)) |
|||
{ |
|||
name += "_" + suffix; |
|||
} |
|||
|
|||
return name; |
|||
} |
|||
|
|||
private async Task CreateVersioningSnapshotAsync(CloudBlockBlob blob, string id, long version) |
|||
{ |
|||
var metadata = new Dictionary<string, string>(); |
|||
metadata.Add(AssetVersion, version.ToString()); |
|||
metadata.Add(AssetId, id); |
|||
await blob.CreateSnapshotAsync(metadata, |
|||
null, null, null); |
|||
} |
|||
|
|||
private async Task<CloudBlockBlob> FindSnapshotAsync(string id, long requestedVersion) |
|||
{ |
|||
CloudBlockBlob resultBlob = null; |
|||
BlobContinuationToken token = null; |
|||
do |
|||
{ |
|||
var listingResult = await blobContainer.ListBlobsSegmentedAsync(null, true, |
|||
BlobListingDetails.Snapshots, 10, token, null, null); |
|||
token = listingResult.ContinuationToken; |
|||
|
|||
foreach (CloudBlob snapshotBlob in listingResult.Results.Cast<CloudBlob>()) |
|||
{ |
|||
if (snapshotBlob.Metadata.TryGetValue(AssetVersion, out string snapshotVersionStr) |
|||
&& snapshotBlob.Metadata.TryGetValue(AssetId, out string snapshotAssetId) |
|||
&& long.TryParse(snapshotVersionStr, out long snapshotVersion) |
|||
&& snapshotVersion == requestedVersion |
|||
&& snapshotAssetId == id) |
|||
{ |
|||
resultBlob = snapshotBlob as CloudBlockBlob; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
while (token != null); |
|||
|
|||
return resultBlob; |
|||
} |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
// ==========================================================================
|
|||
// BlobContainerProvider.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Microsoft.WindowsAzure.Storage.Blob; |
|||
|
|||
namespace Squidex.Infrastructure.AzureStorage |
|||
{ |
|||
public class BlobContainerProvider : IBlobContainerProvider |
|||
{ |
|||
private readonly IStorageAccountManager accountManager; |
|||
|
|||
public BlobContainerProvider(IStorageAccountManager accountManager) |
|||
{ |
|||
this.accountManager = accountManager; |
|||
} |
|||
|
|||
public async Task<CloudBlobContainer> GetContainerAsync(string name) |
|||
{ |
|||
var client = accountManager.CreateCloudBlobClient(); |
|||
var saneName = name.Replace("@", "-").Replace(".", "-"); |
|||
var container = client.GetContainerReference(saneName); |
|||
await container.CreateIfNotExistsAsync(); |
|||
|
|||
return container; |
|||
} |
|||
} |
|||
} |
|||
@ -1,18 +0,0 @@ |
|||
// ==========================================================================
|
|||
// IBlobContainerProvider.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Microsoft.WindowsAzure.Storage.Blob; |
|||
|
|||
namespace Squidex.Infrastructure.AzureStorage |
|||
{ |
|||
public interface IBlobContainerProvider |
|||
{ |
|||
Task<CloudBlobContainer> GetContainerAsync(string name); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue