Browse Source

Fix serialization issue for uploading app image.

pull/419/head
Sebastian Stehle 6 years ago
parent
commit
66addb637a
  1. 23
      src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs
  2. 5
      src/Squidex.Domain.Apps.Entities/Apps/Commands/UploadAppImage.cs
  3. 53
      src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs
  4. 20
      src/Squidex.Web/FileExtensions.cs
  5. 2
      src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs
  6. 4
      src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs
  7. 8
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppCommandMiddlewareTests.cs

23
src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs

@ -42,14 +42,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{ {
if (context.Command is UploadAppImage uploadImage) if (context.Command is UploadAppImage uploadImage)
{ {
var image = await assetThumbnailGenerator.GetImageInfoAsync(uploadImage.File()); await UploadAsync(uploadImage);
if (image == null)
{
throw new ValidationException("File is not an image.");
}
await assetStore.UploadAsync(uploadImage.AppId.ToString(), uploadImage.File(), true);
} }
await ExecuteCommandAsync(context); await ExecuteCommandAsync(context);
@ -61,5 +54,19 @@ namespace Squidex.Domain.Apps.Entities.Apps
await next(); await next();
} }
private async Task UploadAsync(UploadAppImage uploadImage)
{
var file = uploadImage.File;
var image = await assetThumbnailGenerator.GetImageInfoAsync(file.OpenRead());
if (image == null)
{
throw new ValidationException("File is not an image.");
}
await assetStore.UploadAsync(uploadImage.AppId.ToString(), file.OpenRead(), true);
}
} }
} }

5
src/Squidex.Domain.Apps.Entities/Apps/Commands/UploadAppImage.cs

@ -5,9 +5,8 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.IO;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
using Squidex.Infrastructure.Assets;
namespace Squidex.Domain.Apps.Entities.Apps.Commands namespace Squidex.Domain.Apps.Entities.Apps.Commands
{ {
@ -15,6 +14,6 @@ namespace Squidex.Domain.Apps.Entities.Apps.Commands
{ {
public AppImage Image { get; set; } public AppImage Image { get; set; }
public Func<Stream> File { get; set; } public AssetFile File { get; set; }
} }
} }

53
src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs

@ -50,17 +50,14 @@ namespace Squidex.Domain.Apps.Entities.Assets
public override async Task HandleAsync(CommandContext context, Func<Task> next) public override async Task HandleAsync(CommandContext context, Func<Task> next)
{ {
var tempFile = context.ContextId.ToString();
switch (context.Command) switch (context.Command)
{ {
case CreateAsset createAsset: case CreateAsset createAsset:
{ {
if (createAsset.Tags == null)
{
createAsset.Tags = new HashSet<string>();
}
await EnrichWithImageInfosAsync(createAsset); await EnrichWithImageInfosAsync(createAsset);
await EnrichWithHashAndUploadAsync(createAsset, context); await EnrichWithHashAndUploadAsync(createAsset, tempFile);
try try
{ {
@ -68,32 +65,30 @@ namespace Squidex.Domain.Apps.Entities.Assets
foreach (var existing in existings) foreach (var existing in existings)
{ {
if (IsDuplicate(createAsset, existing)) if (IsDuplicate(existing, createAsset.File))
{ {
var result = new AssetCreatedResult(existing, true); var result = new AssetCreatedResult(existing, true);
context.Complete(result); context.Complete(result);
await next(); await next();
return; return;
} }
} }
foreach (var tagGenerator in tagGenerators) GenerateTags(createAsset);
{
tagGenerator.GenerateTags(createAsset, createAsset.Tags);
}
await HandleCoreAsync(context, next); await HandleCoreAsync(context, next);
var asset = context.PlainResult as IEnrichedAssetEntity; var asset = context.Result<IEnrichedAssetEntity>();
context.Complete(new AssetCreatedResult(asset, false)); context.Complete(new AssetCreatedResult(asset, false));
await assetStore.CopyAsync(context.ContextId.ToString(), createAsset.AssetId.ToString(), asset.FileVersion, null); await assetStore.CopyAsync(tempFile, createAsset.AssetId.ToString(), asset.FileVersion, null);
} }
finally finally
{ {
await assetStore.DeleteAsync(context.ContextId.ToString()); await assetStore.DeleteAsync(tempFile);
} }
break; break;
@ -102,19 +97,19 @@ namespace Squidex.Domain.Apps.Entities.Assets
case UpdateAsset updateAsset: case UpdateAsset updateAsset:
{ {
await EnrichWithImageInfosAsync(updateAsset); await EnrichWithImageInfosAsync(updateAsset);
await EnrichWithHashAndUploadAsync(updateAsset, context); await EnrichWithHashAndUploadAsync(updateAsset, tempFile);
try try
{ {
await HandleCoreAsync(context, next); await HandleCoreAsync(context, next);
var asset = context.PlainResult as IAssetEntity; var asset = context.Result<IEnrichedAssetEntity>();
await assetStore.CopyAsync(context.ContextId.ToString(), updateAsset.AssetId.ToString(), asset.FileVersion, null); await assetStore.CopyAsync(tempFile, updateAsset.AssetId.ToString(), asset.FileVersion, null);
} }
finally finally
{ {
await assetStore.DeleteAsync(context.ContextId.ToString()); await assetStore.DeleteAsync(tempFile);
} }
break; break;
@ -122,7 +117,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
default: default:
await HandleCoreAsync(context, next); await HandleCoreAsync(context, next);
break; break;
} }
} }
@ -139,9 +133,9 @@ namespace Squidex.Domain.Apps.Entities.Assets
} }
} }
private static bool IsDuplicate(CreateAsset createAsset, IAssetEntity asset) private static bool IsDuplicate(IAssetEntity asset, AssetFile file)
{ {
return asset != null && asset.FileName == createAsset.File.FileName && asset.FileSize == createAsset.File.FileSize; return asset?.FileName == file.FileName && asset.FileSize == file.FileSize;
} }
private async Task EnrichWithImageInfosAsync(UploadAssetCommand command) private async Task EnrichWithImageInfosAsync(UploadAssetCommand command)
@ -149,14 +143,27 @@ namespace Squidex.Domain.Apps.Entities.Assets
command.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(command.File.OpenRead()); command.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(command.File.OpenRead());
} }
private async Task EnrichWithHashAndUploadAsync(UploadAssetCommand command, CommandContext context) private async Task EnrichWithHashAndUploadAsync(UploadAssetCommand command, string tempFile)
{ {
using (var hashStream = new HasherStream(command.File.OpenRead(), HashAlgorithmName.SHA256)) using (var hashStream = new HasherStream(command.File.OpenRead(), HashAlgorithmName.SHA256))
{ {
await assetStore.UploadAsync(context.ContextId.ToString(), hashStream); await assetStore.UploadAsync(tempFile, hashStream);
command.FileHash = $"{hashStream.GetHashStringAndReset()}{command.File.FileName}{command.File.FileSize}".Sha256Base64(); command.FileHash = $"{hashStream.GetHashStringAndReset()}{command.File.FileName}{command.File.FileSize}".Sha256Base64();
} }
} }
private void GenerateTags(CreateAsset createAsset)
{
if (createAsset.Tags == null)
{
createAsset.Tags = new HashSet<string>();
}
foreach (var tagGenerator in tagGenerators)
{
tagGenerator.GenerateTags(createAsset, createAsset.Tags);
}
}
} }
} }

20
src/Squidex.Web/FileExtensions.cs

@ -0,0 +1,20 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Microsoft.AspNetCore.Http;
using Squidex.Infrastructure.Assets;
namespace Squidex.Web
{
public static class FileExtensions
{
public static AssetFile ToAssetFile(this IFormFile formFile)
{
return new AssetFile(formFile.FileName, formFile.ContentType, formFile.Length, formFile.OpenReadStream);
}
}
}

2
src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs

@ -284,7 +284,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
throw new ValidationException("Cannot create asset.", error); throw new ValidationException("Cannot create asset.", error);
} }
return new UploadAppImage { File = file[0].OpenReadStream, Image = new AppImage(file[0].ContentType) }; return new UploadAppImage { File = file[0].ToAssetFile(), Image = new AppImage(file[0].ContentType) };
} }
private static FileStream GetTempStream() private static FileStream GetTempStream()

4
src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs

@ -313,9 +313,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
throw new ValidationException("Cannot create asset.", error); throw new ValidationException("Cannot create asset.", error);
} }
var assetFile = new AssetFile(formFile.FileName, formFile.ContentType, formFile.Length, formFile.OpenReadStream); return formFile.ToAssetFile();
return assetFile;
} }
} }
} }

8
tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppCommandMiddlewareTests.cs

@ -69,7 +69,9 @@ namespace Squidex.Domain.Apps.Entities.Apps
{ {
var stream = new MemoryStream(); var stream = new MemoryStream();
var command = CreateCommand(new UploadAppImage { AppId = appId, File = () => stream }); var file = new AssetFile("name.jpg", "image/jpg", 1024, () => stream);
var command = CreateCommand(new UploadAppImage { AppId = appId, File = file });
var context = CreateContextForCommand(command); var context = CreateContextForCommand(command);
A.CallTo(() => assetThumbnailGenerator.GetImageInfoAsync(stream)) A.CallTo(() => assetThumbnailGenerator.GetImageInfoAsync(stream))
@ -86,7 +88,9 @@ namespace Squidex.Domain.Apps.Entities.Apps
{ {
var stream = new MemoryStream(); var stream = new MemoryStream();
var command = CreateCommand(new UploadAppImage { AppId = appId, File = () => stream }); var file = new AssetFile("name.jpg", "image/jpg", 1024, () => stream);
var command = CreateCommand(new UploadAppImage { AppId = appId, File = file });
var context = CreateContextForCommand(command); var context = CreateContextForCommand(command);
A.CallTo(() => assetThumbnailGenerator.GetImageInfoAsync(stream)) A.CallTo(() => assetThumbnailGenerator.GetImageInfoAsync(stream))

Loading…
Cancel
Save