mirror of https://github.com/Squidex/squidex.git
17 changed files with 317 additions and 58 deletions
@ -0,0 +1,90 @@ |
|||
// ==========================================================================
|
|||
// GoogleCloudAssetStore.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using System.Net; |
|||
using System.Threading.Tasks; |
|||
using Google; |
|||
using Google.Cloud.Storage.V1; |
|||
using Squidex.Infrastructure.Assets; |
|||
|
|||
namespace Squidex.Infrastructure.GoogleCloud |
|||
{ |
|||
public sealed class GoogleCloudAssetStore : IAssetStore, IExternalSystem |
|||
{ |
|||
private readonly string bucketName; |
|||
private StorageClient storageClient; |
|||
|
|||
public GoogleCloudAssetStore(string bucketName) |
|||
{ |
|||
Guard.NotNullOrEmpty(bucketName, nameof(bucketName)); |
|||
|
|||
this.bucketName = bucketName; |
|||
} |
|||
|
|||
public void Connect() |
|||
{ |
|||
try |
|||
{ |
|||
storageClient = StorageClient.Create(); |
|||
|
|||
storageClient.GetBucket(bucketName); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
throw new ConfigurationException($"Cannot connect to google cloud bucket '${bucketName}'.", ex); |
|||
} |
|||
} |
|||
|
|||
public async Task DownloadAsync(Guid id, long version, string suffix, Stream stream) |
|||
{ |
|||
var objectName = GetObjectName(id, version, suffix); |
|||
|
|||
try |
|||
{ |
|||
await storageClient.DownloadObjectAsync(bucketName, objectName, stream); |
|||
} |
|||
catch (GoogleApiException ex) |
|||
{ |
|||
if (ex.HttpStatusCode == HttpStatusCode.NotFound) |
|||
{ |
|||
throw new AssetNotFoundException($"Asset {id}, {version} not found.", ex); |
|||
} |
|||
else |
|||
{ |
|||
throw; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public async Task UploadAsync(Guid id, long version, string suffix, Stream stream) |
|||
{ |
|||
var objectName = GetObjectName(id, version, suffix); |
|||
|
|||
await storageClient.UploadObjectAsync(bucketName, objectName, "application/octet-stream", stream); |
|||
} |
|||
|
|||
private string GetObjectName(Guid id, long version, string suffix) |
|||
{ |
|||
if (storageClient == null) |
|||
{ |
|||
throw new InvalidOperationException("No connection established yet."); |
|||
} |
|||
|
|||
var name = $"{id}_{version}"; |
|||
|
|||
if (!string.IsNullOrWhiteSpace(suffix)) |
|||
{ |
|||
name += "_" + suffix; |
|||
} |
|||
|
|||
return name; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard1.6</TargetFramework> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
|||
<DebugType>full</DebugType> |
|||
<DebugSymbols>True</DebugSymbols> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<PackageReference Include="Google.Cloud.Storage.V1" Version="1.0.0" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,29 @@ |
|||
// ==========================================================================
|
|||
// AssetNotFoundException.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Infrastructure.Assets |
|||
{ |
|||
public class AssetNotFoundException : Exception |
|||
{ |
|||
public AssetNotFoundException() |
|||
{ |
|||
} |
|||
|
|||
public AssetNotFoundException(string message) |
|||
: base(message) |
|||
{ |
|||
} |
|||
|
|||
public AssetNotFoundException(string message, Exception inner) |
|||
: base(message, inner) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
// ==========================================================================
|
|||
// FileCallbackResult.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
|
|||
namespace Squidex.Pipeline |
|||
{ |
|||
public class FileCallbackResult : FileResult |
|||
{ |
|||
private readonly Func<Stream, Task> callback; |
|||
|
|||
public Func<Stream, Task> Callback |
|||
{ |
|||
get { return callback; } |
|||
} |
|||
|
|||
public FileCallbackResult(string contentType, string name, Func<Stream, Task> callback) |
|||
: base(contentType) |
|||
{ |
|||
FileDownloadName = name; |
|||
|
|||
this.callback = callback; |
|||
} |
|||
|
|||
public override Task ExecuteResultAsync(ActionContext context) |
|||
{ |
|||
var executor = context.HttpContext.RequestServices.GetRequiredService<FileCallbackResultExecutor>(); |
|||
|
|||
return executor.ExecuteAsync(context, this); |
|||
} |
|||
} |
|||
} |
|||
|
|||
#pragma warning restore 1573
|
|||
@ -0,0 +1,38 @@ |
|||
// ==========================================================================
|
|||
// FileCallbackResultExecutor.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Internal; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace Squidex.Pipeline |
|||
{ |
|||
public sealed class FileCallbackResultExecutor : FileResultExecutorBase |
|||
{ |
|||
public FileCallbackResultExecutor(ILoggerFactory loggerFactory) |
|||
: base(CreateLogger<VirtualFileResultExecutor>(loggerFactory)) |
|||
{ |
|||
} |
|||
|
|||
public async Task ExecuteAsync(ActionContext context, FileCallbackResult result) |
|||
{ |
|||
try |
|||
{ |
|||
SetHeadersAndLog(context, result); |
|||
|
|||
await result.Callback(context.HttpContext.Response.Body); |
|||
} |
|||
catch |
|||
{ |
|||
context.HttpContext.Response.Headers.Clear(); |
|||
context.HttpContext.Response.StatusCode = 404; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue