예제 #1
0
        private async Task <IdentityResult> UpdatePictureAsync(List <IFormFile> file, UserWithClaims user)
        {
            if (file.Count != 1)
            {
                return(IdentityResult.Failed(new IdentityError {
                    Description = "Please upload a single file."
                }));
            }

            using (var thumbnailStream = new MemoryStream())
            {
                try
                {
                    await assetThumbnailGenerator.CreateThumbnailAsync(file[0].OpenReadStream(), thumbnailStream, 128, 128, "Crop");

                    thumbnailStream.Position = 0;
                }
                catch
                {
                    return(IdentityResult.Failed(new IdentityError {
                        Description = "Picture is not a valid image."
                    }));
                }

                await userPictureStore.UploadAsync(user.Id, thumbnailStream);
            }

            return(await userManager.UpdateSafeAsync(user.Identity, new UserValues { PictureUrl = SquidexClaimTypes.PictureUrlStore }));
        }
예제 #2
0
        private async Task UploadResizedAsync(IFormFile file, string id,
                                              CancellationToken ct)
        {
            await using var assetResized = TempAssetFile.Create(file.ToAssetFile());

            var resizeOptions = new ResizeOptions
            {
                TargetWidth  = 128,
                TargetHeight = 128
            };

            try
            {
                await using (var originalStream = file.OpenReadStream())
                {
                    await using (var resizeStream = assetResized.OpenWrite())
                    {
                        await assetThumbnailGenerator.CreateThumbnailAsync(originalStream, file.ContentType, resizeStream, resizeOptions, ct);
                    }
                }
            }
            catch
            {
                throw new ValidationException(T.Get("validation.notAnImage"));
            }

            await using (var resizeStream = assetResized.OpenWrite())
            {
                await userPictureStore.UploadAsync(id, resizeStream, ct);
            }
        }
예제 #3
0
        private async Task UpdatePictureAsync(List <IFormFile> file, string id)
        {
            if (file.Count != 1)
            {
                throw new ValidationException(T.Get("validation.onlyOneFile"));
            }

            using (var thumbnailStream = new MemoryStream())
            {
                try
                {
                    await assetThumbnailGenerator.CreateThumbnailAsync(file[0].OpenReadStream(), thumbnailStream, ResizeOptions);

                    thumbnailStream.Position = 0;
                }
                catch
                {
                    throw new ValidationException(T.Get("validation.notAnImage"));
                }

                await userPictureStore.UploadAsync(id, thumbnailStream);
            }

            var update = new UserValues {
                PictureUrl = SquidexClaimTypes.PictureUrlStore
            };

            await userService.UpdateAsync(id, update);
        }
예제 #4
0
        private async Task <IdentityResult> UpdatePictureAsync(List <IFormFile> file, IdentityUser user)
        {
            if (file.Count != 1)
            {
                var description = T.Get("validation.onlyOneFile");

                return(IdentityResult.Failed(new IdentityError {
                    Code = "PictureNotOneFile", Description = description
                }));
            }

            using (var thumbnailStream = new MemoryStream())
            {
                try
                {
                    await assetThumbnailGenerator.CreateThumbnailAsync(file[0].OpenReadStream(), thumbnailStream, ResizeOptions);

                    thumbnailStream.Position = 0;
                }
                catch
                {
                    var description = T.Get("validation.notAnImage");

                    return(IdentityResult.Failed(new IdentityError {
                        Code = "PictureNotAnImage", Description = description
                    }));
                }

                await userPictureStore.UploadAsync(user.Id, thumbnailStream);
            }

            return(await userManager.UpdateSafeAsync(user, new UserValues { PictureUrl = SquidexClaimTypes.PictureUrlStore }));
        }
예제 #5
0
        public Task <IActionResult> UploadPicture(List <IFormFile> file)
        {
            return(MakeChangeAsync(async user =>
            {
                if (file.Count != 1)
                {
                    return IdentityResult.Failed(new IdentityError {
                        Description = "Please upload a single file."
                    });
                }

                var thumbnailStream = new MemoryStream();
                try
                {
                    await assetThumbnailGenerator.CreateThumbnailAsync(file[0].OpenReadStream(), thumbnailStream, 128, 128, "Crop");

                    thumbnailStream.Position = 0;
                }
                catch
                {
                    return IdentityResult.Failed(new IdentityError {
                        Description = "Picture is not a valid image."
                    });
                }

                await userPictureStore.UploadAsync(user.Id, thumbnailStream);

                user.SetPictureUrlToStore();

                return await userManager.UpdateAsync(user);
            }, "Picture uploaded successfully."));
        }
예제 #6
0
        private async Task <IdentityResult> UpdatePictureAsync(List <IFormFile> file, IUser user)
        {
            if (file.Count != 1)
            {
                return(IdentityResult.Failed(new IdentityError {
                    Description = "Please upload a single file."
                }));
            }

            var thumbnailStream = new MemoryStream();

            try
            {
                await assetThumbnailGenerator.CreateThumbnailAsync(file[0].OpenReadStream(), thumbnailStream, 128, 128, "Crop");

                thumbnailStream.Position = 0;
            }
            catch
            {
                return(IdentityResult.Failed(new IdentityError {
                    Description = "Picture is not a valid image."
                }));
            }

            await userPictureStore.UploadAsync(user.Id, thumbnailStream);

            user.SetPictureUrlToStore();

            return(await userManager.UpdateAsync(user));
        }
예제 #7
0
        private async Task ResizeAsync(HttpContext context)
        {
            await using var tempStream = GetTempStream();

            await context.Request.Body.CopyToAsync(tempStream, context.RequestAborted);

            tempStream.Position = 0;

            try
            {
                var options = ResizeOptions.Parse(context.Request.Query.ToDictionary(x => x.Key, x => x.Value.ToString()));

                await assetThumbnailGenerator.CreateThumbnailAsync(
                    tempStream,
                    context.Request.ContentType ?? "image/png",
                    context.Response.Body, options,
                    context.RequestAborted);
            }
            catch (Exception ex)
            {
                var log = context.RequestServices.GetRequiredService <ISemanticLog>();

                log.LogError(ex, w => w
                             .WriteProperty("action", "Resize")
                             .WriteProperty("status", "Failed"));

                context.Response.StatusCode = 400;
            }
        }
예제 #8
0
        private async Task ResizeAsync(IAssetEntity asset, Stream bodyStream, string fileName, ResizeOptions resizeOptions, bool overwrite, CancellationToken ct)
        {
            using (Profiler.Trace("Resize"))
            {
                using (var sourceStream = GetTempStream())
                {
                    using (var destinationStream = GetTempStream())
                    {
                        using (Profiler.Trace("ResizeDownload"))
                        {
                            await assetFileStore.DownloadAsync(asset.Id, asset.FileVersion, sourceStream);

                            sourceStream.Position = 0;
                        }

                        using (Profiler.Trace("ResizeImage"))
                        {
                            await assetThumbnailGenerator.CreateThumbnailAsync(sourceStream, destinationStream, resizeOptions);

                            destinationStream.Position = 0;
                        }

                        using (Profiler.Trace("ResizeUpload"))
                        {
                            await assetStore.UploadAsync(fileName, destinationStream, overwrite);

                            destinationStream.Position = 0;
                        }

                        await destinationStream.CopyToAsync(bodyStream, ct);
                    }
                }
            }
        }
예제 #9
0
파일: Program.cs 프로젝트: Squidex/assets
 public async Task Resize()
 {
     await generator.CreateThumbnailAsync(source, "image/png", destination, new ResizeOptions
     {
         TargetHeight = 100,
         TargetWidth  = 100
     });
 }
예제 #10
0
        public IActionResult GetImage(string app)
        {
            if (App.Image == null)
            {
                return(NotFound());
            }

            var etag = App.Image.Etag;

            Response.Headers[HeaderNames.ETag] = etag;

            var callback = new FileCallback(async(body, range, ct) =>
            {
                var resizedAsset = $"{App.Id}_{etag}_Resized";

                try
                {
                    await assetStore.DownloadAsync(resizedAsset, body, ct: ct);
                }
                catch (AssetNotFoundException)
                {
                    using (Profiler.Trace("Resize"))
                    {
                        using (var sourceStream = GetTempStream())
                        {
                            using (var destinationStream = GetTempStream())
                            {
                                using (Profiler.Trace("ResizeDownload"))
                                {
                                    await appImageStore.DownloadAsync(App.Id, sourceStream);
                                    sourceStream.Position = 0;
                                }

                                using (Profiler.Trace("ResizeImage"))
                                {
                                    await assetThumbnailGenerator.CreateThumbnailAsync(sourceStream, destinationStream, ResizeOptions);
                                    destinationStream.Position = 0;
                                }

                                using (Profiler.Trace("ResizeUpload"))
                                {
                                    await assetStore.UploadAsync(resizedAsset, destinationStream);
                                    destinationStream.Position = 0;
                                }

                                await destinationStream.CopyToAsync(body, ct);
                            }
                        }
                    }
                }
            });

            return(new FileCallbackResult(App.Image.MimeType, callback)
            {
                ErrorAs404 = true
            });
        }
예제 #11
0
        public IActionResult GetImage(string app)
        {
            if (App.Image == null)
            {
                return(NotFound());
            }

            var etag = App.Image.Etag;

            Response.Headers[HeaderNames.ETag] = etag;

            var handler = new Func <Stream, Task>(async bodyStream =>
            {
                var assetId        = App.Id.ToString();
                var assetResizedId = $"{assetId}_{etag}_Resized";

                try
                {
                    await assetStore.DownloadAsync(assetResizedId, bodyStream);
                }
                catch (AssetNotFoundException)
                {
                    using (Profiler.Trace("Resize"))
                    {
                        using (var sourceStream = GetTempStream())
                        {
                            using (var destinationStream = GetTempStream())
                            {
                                using (Profiler.Trace("ResizeDownload"))
                                {
                                    await assetStore.DownloadAsync(assetId, sourceStream);
                                    sourceStream.Position = 0;
                                }

                                using (Profiler.Trace("ResizeImage"))
                                {
                                    await assetThumbnailGenerator.CreateThumbnailAsync(sourceStream, destinationStream, 150, 150, "Crop");
                                    destinationStream.Position = 0;
                                }

                                using (Profiler.Trace("ResizeUpload"))
                                {
                                    await assetStore.UploadAsync(assetResizedId, destinationStream);
                                    destinationStream.Position = 0;
                                }

                                await destinationStream.CopyToAsync(bodyStream);
                            }
                        }
                    }
                }
            });

            return(new FileCallbackResult(App.Image.MimeType, null, true, handler));
        }
예제 #12
0
        private async Task ResizeAsync(IAssetEntity asset, Stream bodyStream, ResizeOptions resizeOptions, bool overwrite, CancellationToken ct)
        {
            var suffix = resizeOptions.ToString();

            using (Profiler.Trace("Resize"))
            {
                using (var sourceStream = GetTempStream())
                {
                    using (var destinationStream = GetTempStream())
                    {
                        using (Profiler.Trace("ResizeDownload"))
                        {
                            await assetFileStore.DownloadAsync(asset.AppId.Id, asset.Id, asset.FileVersion, null, sourceStream);

                            sourceStream.Position = 0;
                        }

                        using (Profiler.Trace("ResizeImage"))
                        {
                            try
                            {
                                await assetThumbnailGenerator.CreateThumbnailAsync(sourceStream, destinationStream, resizeOptions);

                                destinationStream.Position = 0;
                            }
                            catch
                            {
                                sourceStream.Position = 0;
                                await sourceStream.CopyToAsync(destinationStream);
                            }
                        }

                        try
                        {
                            using (Profiler.Trace("ResizeUpload"))
                            {
                                await assetFileStore.UploadAsync(asset.AppId.Id, asset.Id, asset.FileVersion, suffix, destinationStream, overwrite);

                                destinationStream.Position = 0;
                            }
                        }
                        catch (AssetAlreadyExistsException)
                        {
                            destinationStream.Position = 0;
                        }

                        await destinationStream.CopyToAsync(bodyStream, ct);
                    }
                }
            }
        }
예제 #13
0
        public async Task <IActionResult> GetAssetContent(Guid id, [FromQuery] long version = EtagVersion.Any, [FromQuery] int?width = null, [FromQuery] int?height = null, [FromQuery] string mode = null)
        {
            var entity = await assetRepository.FindAssetAsync(id);

            if (entity == null || entity.FileVersion < version || width == 0 || height == 0)
            {
                return(NotFound());
            }

            Response.Headers["ETag"] = entity.FileVersion.ToString();

            return(new FileCallbackResult(entity.MimeType, entity.FileName, async bodyStream =>
            {
                var assetId = entity.Id.ToString();

                if (entity.IsImage && (width.HasValue || height.HasValue))
                {
                    var assetSuffix = $"{width}_{height}_{mode}";

                    try
                    {
                        await assetStore.DownloadAsync(assetId, entity.FileVersion, assetSuffix, bodyStream);
                    }
                    catch (AssetNotFoundException)
                    {
                        using (var sourceStream = GetTempStream())
                        {
                            using (var destinationStream = GetTempStream())
                            {
                                await assetStore.DownloadAsync(assetId, entity.FileVersion, null, sourceStream);
                                sourceStream.Position = 0;

                                await assetThumbnailGenerator.CreateThumbnailAsync(sourceStream, destinationStream, width, height, mode);
                                destinationStream.Position = 0;

                                await assetStore.UploadAsync(assetId, entity.FileVersion, assetSuffix, destinationStream);
                                destinationStream.Position = 0;

                                await destinationStream.CopyToAsync(bodyStream);
                            }
                        }
                    }
                }
                else
                {
                    await assetStore.DownloadAsync(assetId, entity.FileVersion, null, bodyStream);
                }
            }));
        }
예제 #14
0
        public async Task <IActionResult> GetAssetContent(string app, Guid id, [FromQuery] int version = -1, [FromQuery] int?width = null, [FromQuery] int?height = null, [FromQuery] string mode = null)
        {
            var asset = await assetRepository.FindAssetAsync(id);

            if (asset == null || asset.FileVersion < version || width == 0 || height == 0)
            {
                return(NotFound());
            }

            return(new FileCallbackResult(asset.MimeType, asset.FileName, async bodyStream =>
            {
                if (asset.IsImage && (width.HasValue || height.HasValue))
                {
                    var suffix = $"{width}_{height}_{mode}";

                    try
                    {
                        await assetStorage.DownloadAsync(asset.Id, asset.FileVersion, suffix, bodyStream);
                    }
                    catch (AssetNotFoundException)
                    {
                        using (var sourceStream = GetTempStream())
                        {
                            using (var destinationStream = GetTempStream())
                            {
                                await assetStorage.DownloadAsync(asset.Id, asset.FileVersion, null, sourceStream);
                                sourceStream.Position = 0;

                                await assetThumbnailGenerator.CreateThumbnailAsync(sourceStream, destinationStream, width, height, mode);
                                destinationStream.Position = 0;

                                await assetStorage.UploadAsync(asset.Id, asset.FileVersion, suffix, destinationStream);
                                destinationStream.Position = 0;

                                await destinationStream.CopyToAsync(bodyStream);
                            }
                        }
                    }
                }

                await assetStorage.DownloadAsync(asset.Id, asset.FileVersion, null, bodyStream);
            }));
        }
예제 #15
0
        private IActionResult DeliverAsset(IAssetEntity entity, long version, int?width, int?height, int?quality, string mode, int download = 1)
        {
            if (entity == null || entity.FileVersion < version || width == 0 || height == 0 || quality == 0)
            {
                return(NotFound());
            }

            Response.Headers[HeaderNames.ETag] = entity.FileVersion.ToString();

            var handler = new Func <Stream, Task>(async bodyStream =>
            {
                var assetId = entity.Id.ToString();

                if (entity.IsImage && (width.HasValue || height.HasValue || quality.HasValue))
                {
                    var assetSuffix = $"{width}_{height}_{mode}";

                    if (quality.HasValue)
                    {
                        assetSuffix += $"_{quality}";
                    }

                    try
                    {
                        await assetStore.DownloadAsync(assetId, entity.FileVersion, assetSuffix, bodyStream);
                    }
                    catch (AssetNotFoundException)
                    {
                        using (Profiler.Trace("Resize"))
                        {
                            using (var sourceStream = GetTempStream())
                            {
                                using (var destinationStream = GetTempStream())
                                {
                                    using (Profiler.Trace("ResizeDownload"))
                                    {
                                        await assetStore.DownloadAsync(assetId, entity.FileVersion, null, sourceStream);
                                        sourceStream.Position = 0;
                                    }

                                    using (Profiler.Trace("ResizeImage"))
                                    {
                                        await assetThumbnailGenerator.CreateThumbnailAsync(sourceStream, destinationStream, width, height, mode, quality);
                                        destinationStream.Position = 0;
                                    }

                                    using (Profiler.Trace("ResizeUpload"))
                                    {
                                        await assetStore.UploadAsync(assetId, entity.FileVersion, assetSuffix, destinationStream);
                                        destinationStream.Position = 0;
                                    }

                                    await destinationStream.CopyToAsync(bodyStream);
                                }
                            }
                        }
                    }
                }
                else
                {
                    await assetStore.DownloadAsync(assetId, entity.FileVersion, null, bodyStream);
                }
            });

            if (download == 1)
            {
                return(new FileCallbackResult(entity.MimeType, entity.FileName, true, handler));
            }
            else
            {
                return(new FileCallbackResult(entity.MimeType, null, true, handler));
            }
        }
예제 #16
0
        private IActionResult DeliverAsset(IAssetEntity?asset, AssetQuery query)
        {
            query ??= new AssetQuery();

            if (asset == null || asset.FileVersion < query.Version)
            {
                return(NotFound());
            }

            if (asset.IsProtected && !this.HasPermission(Permissions.AppAssetsRead))
            {
                return(StatusCode(403));
            }

            var fileVersion = query.Version;

            if (fileVersion <= EtagVersion.Any)
            {
                fileVersion = asset.FileVersion;
            }

            Response.Headers[HeaderNames.ETag] = fileVersion.ToString();

            if (query.CacheDuration > 0)
            {
                Response.Headers[HeaderNames.CacheControl] = $"public,max-age={query.CacheDuration}";
            }

            var handler = new Func <Stream, Task>(async bodyStream =>
            {
                if (asset.Type == AssetType.Image && query.ShouldResize())
                {
                    var resizedAsset = $"{asset.Id}_{asset.FileVersion}_{query.Width}_{query.Height}_{query.Mode}";

                    if (query.Quality.HasValue)
                    {
                        resizedAsset += $"_{query.Quality}";
                    }

                    try
                    {
                        await assetStore.DownloadAsync(resizedAsset, bodyStream);
                    }
                    catch (AssetNotFoundException)
                    {
                        using (Profiler.Trace("Resize"))
                        {
                            using (var sourceStream = GetTempStream())
                            {
                                using (var destinationStream = GetTempStream())
                                {
                                    using (Profiler.Trace("ResizeDownload"))
                                    {
                                        await assetFileStore.DownloadAsync(asset.Id, fileVersion, sourceStream);
                                        sourceStream.Position = 0;
                                    }

                                    using (Profiler.Trace("ResizeImage"))
                                    {
                                        await assetThumbnailGenerator.CreateThumbnailAsync(sourceStream, destinationStream, query.Width, query.Height, query.Mode, query.Quality);
                                        destinationStream.Position = 0;
                                    }

                                    using (Profiler.Trace("ResizeUpload"))
                                    {
                                        await assetStore.UploadAsync(resizedAsset, destinationStream);
                                        destinationStream.Position = 0;
                                    }

                                    await destinationStream.CopyToAsync(bodyStream);
                                }
                            }
                        }
                    }
                }
                else
                {
                    await assetFileStore.DownloadAsync(asset.Id, fileVersion, bodyStream);
                }
            });

            if (query.Download == 1)
            {
                return(new FileCallbackResult(asset.MimeType, asset.FileName, true, handler));
            }
            else
            {
                return(new FileCallbackResult(asset.MimeType, null, true, handler));
            }
        }
예제 #17
0
        private async Task ResizeAsync(IAssetEntity asset, string suffix, Stream target, ResizeOptions resizeOptions, bool overwrite,
                                       CancellationToken ct)
        {
#pragma warning disable MA0040 // Flow the cancellation token
            using var activity = Telemetry.Activities.StartActivity("Resize");

            await using var assetOriginal = new TempAssetFile(asset.FileName, asset.MimeType, 0);
            await using var assetResized  = new TempAssetFile(asset.FileName, asset.MimeType, 0);

            using (Telemetry.Activities.StartActivity("Read"))
            {
                await using (var originalStream = assetOriginal.OpenWrite())
                {
                    await assetFileStore.DownloadAsync(asset.AppId.Id, asset.Id, asset.FileVersion, null, originalStream);
                }
            }

            using (Telemetry.Activities.StartActivity("Resize"))
            {
                try
                {
                    await using (var originalStream = assetOriginal.OpenRead())
                    {
                        await using (var resizeStream = assetResized.OpenWrite())
                        {
                            await assetThumbnailGenerator.CreateThumbnailAsync(originalStream, asset.MimeType, resizeStream, resizeOptions);
                        }
                    }
                }
                catch
                {
                    await using (var originalStream = assetOriginal.OpenRead())
                    {
                        await using (var resizeStream = assetResized.OpenWrite())
                        {
                            await originalStream.CopyToAsync(resizeStream);
                        }
                    }
                }
            }

            using (Telemetry.Activities.StartActivity("Save"))
            {
                try
                {
                    await using (var resizeStream = assetResized.OpenRead())
                    {
                        await assetFileStore.UploadAsync(asset.AppId.Id, asset.Id, asset.FileVersion, suffix, resizeStream, overwrite);
                    }
                }
                catch (AssetAlreadyExistsException)
                {
                    return;
                }
            }

            using (Telemetry.Activities.StartActivity("Write"))
            {
                await using (var resizeStream = assetResized.OpenRead())
                {
                    await resizeStream.CopyToAsync(target, ct);
                }
            }
#pragma warning restore MA0040 // Flow the cancellation token
        }
예제 #18
0
        public async Task Should_convert_between_formats(ImageFormat sourceFormat, ImageFormat targetFormat)
        {
            if (SupportedFormats?.Contains(sourceFormat) == false)
            {
                return;
            }

            if (SupportedFormats?.Contains(targetFormat) == false)
            {
                return;
            }

            var(mimeType, source) = GetImage(sourceFormat);

            await using (var target = GetStream($"transform.{sourceFormat.ToString().ToLowerInvariant()}", targetFormat.ToString().ToLowerInvariant()))
            {
                await sut.CreateThumbnailAsync(source, mimeType, target, new ResizeOptions
                {
                    Format = targetFormat
                });

                target.Position = 0;

                var imageInfo = await sut.GetImageInfoAsync(target, targetFormat.ToMimeType());

                Assert.Equal(targetFormat, imageInfo?.Format);
            }
        }
예제 #19
0
        private async Task ResizeAsync(string resizedAsset, string mimeType, Stream target,
                                       CancellationToken ct)
        {
#pragma warning disable MA0040 // Flow the cancellation token
            using var activity = Telemetry.Activities.StartActivity("Resize");

            await using var assetOriginal = new TempAssetFile(resizedAsset, mimeType, 0);
            await using var assetResized  = new TempAssetFile(resizedAsset, mimeType, 0);

            var resizeOptions = new ResizeOptions
            {
                TargetWidth  = 50,
                TargetHeight = 50
            };

            using (Telemetry.Activities.StartActivity("Read"))
            {
                await using (var originalStream = assetOriginal.OpenWrite())
                {
                    await appImageStore.DownloadAsync(App.Id, originalStream);
                }
            }

            using (Telemetry.Activities.StartActivity("Resize"))
            {
                try
                {
                    await using (var originalStream = assetOriginal.OpenRead())
                    {
                        await using (var resizeStream = assetResized.OpenWrite())
                        {
                            await assetThumbnailGenerator.CreateThumbnailAsync(originalStream, mimeType, resizeStream, resizeOptions);
                        }
                    }
                }
                catch
                {
                    await using (var originalStream = assetOriginal.OpenRead())
                    {
                        await using (var resizeStream = assetResized.OpenWrite())
                        {
                            await originalStream.CopyToAsync(resizeStream);
                        }
                    }
                }
            }

            using (Telemetry.Activities.StartActivity("Save"))
            {
                try
                {
                    await using (var resizeStream = assetResized.OpenRead())
                    {
                        await assetStore.UploadAsync(resizedAsset, resizeStream);
                    }
                }
                catch (AssetAlreadyExistsException)
                {
                    return;
                }
            }

            using (Telemetry.Activities.StartActivity("Write"))
            {
                await using (var resizeStream = assetResized.OpenRead())
                {
                    await resizeStream.CopyToAsync(target, ct);
                }
            }
#pragma warning restore MA0040 // Flow the cancellation token
        }
예제 #20
0
        public async Task <IActionResult> GetAssetContent(string id, string more,
                                                          [FromQuery] long version = EtagVersion.Any,
                                                          [FromQuery] int?width    = null,
                                                          [FromQuery] int?height   = null,
                                                          [FromQuery] int?quality  = null,
                                                          [FromQuery] string mode  = null)
        {
            IAssetEntity entity;

            if (Guid.TryParse(id, out var guid))
            {
                entity = await assetRepository.FindAssetAsync(guid);
            }
            else
            {
                entity = await assetRepository.FindAssetAsync(id);
            }

            if (entity == null || entity.FileVersion < version || width == 0 || height == 0 || quality == 0)
            {
                return(NotFound());
            }

            Response.Headers[HeaderNames.ETag] = entity.FileVersion.ToString();

            return(new FileCallbackResult(entity.MimeType, entity.FileName, true, async bodyStream =>
            {
                var assetId = entity.Id.ToString();

                if (entity.IsImage && (width.HasValue || height.HasValue || quality.HasValue))
                {
                    var assetSuffix = $"{width}_{height}_{mode}";

                    if (quality.HasValue)
                    {
                        assetSuffix += $"_{quality}";
                    }

                    try
                    {
                        await assetStore.DownloadAsync(assetId, entity.FileVersion, assetSuffix, bodyStream);
                    }
                    catch (AssetNotFoundException)
                    {
                        using (Profiler.Trace("Resize"))
                        {
                            using (var sourceStream = GetTempStream())
                            {
                                using (var destinationStream = GetTempStream())
                                {
                                    using (Profiler.Trace("ResizeDownload"))
                                    {
                                        await assetStore.DownloadAsync(assetId, entity.FileVersion, null, sourceStream);
                                        sourceStream.Position = 0;
                                    }

                                    using (Profiler.Trace("ResizeImage"))
                                    {
                                        await assetThumbnailGenerator.CreateThumbnailAsync(sourceStream, destinationStream, width, height, mode, quality);
                                        destinationStream.Position = 0;
                                    }

                                    using (Profiler.Trace("ResizeUpload"))
                                    {
                                        await assetStore.UploadAsync(assetId, entity.FileVersion, assetSuffix, destinationStream);
                                        destinationStream.Position = 0;
                                    }

                                    await destinationStream.CopyToAsync(bodyStream);
                                }
                            }
                        }
                    }
                }
                else
                {
                    await assetStore.DownloadAsync(assetId, entity.FileVersion, null, bodyStream);
                }
            }));
        }