From 30199de4d8b333948ab682cbf828fa421d8fe473 Mon Sep 17 00:00:00 2001 From: Tobias Klika Date: Mon, 24 Aug 2020 15:17:43 +0200 Subject: [PATCH] edit media + replace file in media entity --- zero.Core/Api/MediaUploadApi.cs | 48 +++--- zero.Core/Entities/Media/MediaImageMeta.cs | 4 +- zero.Web.UI/App/editor/editor-component.vue | 9 +- zero.Web.UI/App/pages/media/detail.vue | 3 + zero.Web.UI/App/pages/media/upload.vue | 151 ++++++++++++++++++ zero.Web.UI/App/renderers/media.js | 6 + zero.Web.UI/App/resources/media.js | 4 +- zero.Web/Controllers/MediaController.cs | 15 +- .../Resources/Localization/zero.en-us.json | 3 +- 9 files changed, 216 insertions(+), 27 deletions(-) create mode 100644 zero.Web.UI/App/pages/media/upload.vue diff --git a/zero.Core/Api/MediaUploadApi.cs b/zero.Core/Api/MediaUploadApi.cs index 0fd7a55a..fb9584d0 100644 --- a/zero.Core/Api/MediaUploadApi.cs +++ b/zero.Core/Api/MediaUploadApi.cs @@ -29,7 +29,7 @@ namespace zero.Core.Api private const string UPLOAD_PREFIX = "upload:"; - private string[] ImageExtensions = new[] { ".jpg", ".jpeg", ".png", ".bmp", ".webp", ".jfif" }; + private string[] ImageExtensions = new[] { ".jpg", ".jpeg", ".png", ".bmp", ".webp", ".jfif", ".gif" }; public MediaUploadApi(IPaths paths) @@ -66,6 +66,10 @@ namespace zero.Core.Api await file.CopyToAsync(stream, cancellationToken); } + // set new properties + media.Source = Path.Combine(PATH_PREFIX, media.FileId, media.Name).Replace(Path.DirectorySeparatorChar, PATH_SEPARATOR); + media.Size = file.Length; + // write additional image data + thumbnail if (media.Type == MediaType.Image) { @@ -73,36 +77,44 @@ namespace zero.Core.Api { media.ImageMeta = GetImageMeta(image); - image.Mutate(x => x.Resize(new ResizeOptions() + Image imageFrame = media.ImageMeta.Frames > 1 ? image.Frames.CloneFrame(0) : image; + + media.PreviewSource = SaveThumbnail(media, imageFrame, PREVIEW_EXTENSION, new ResizeOptions() { Size = new Size(210, 210), Mode = ResizeMode.Min - })); + }); - string thumbFileName = media.Name.TrimEnd(extension) + PREVIEW_EXTENSION + extension; - image.Save(Path.Combine(Paths.Media, media.FileId, thumbFileName)); - media.PreviewSource = Path.Combine(PATH_PREFIX, media.FileId, thumbFileName).Replace(Path.DirectorySeparatorChar, PATH_SEPARATOR); - - image.Mutate(x => x.Resize(new ResizeOptions() + media.ThumbnailSource = SaveThumbnail(media, imageFrame, THUMB_EXTENSION, new ResizeOptions() { Size = new Size(100, 100), Mode = ResizeMode.Max - })); - - thumbFileName = media.Name.TrimEnd(extension) + THUMB_EXTENSION + extension; - image.Save(Path.Combine(Paths.Media, media.FileId, thumbFileName)); - media.ThumbnailSource = Path.Combine(PATH_PREFIX, media.FileId, thumbFileName).Replace(Path.DirectorySeparatorChar, PATH_SEPARATOR); + }); } } - // set new properties - media.Source = Path.Combine(PATH_PREFIX, media.FileId, media.Name).Replace(Path.DirectorySeparatorChar, PATH_SEPARATOR); - media.Size = file.Length; - return media; } + /// + /// Saves a thumbnail of an image + /// + string SaveThumbnail(IMedia media, Image image, string extensionPrefix, ResizeOptions resizeOptions) + { + string extension = Path.GetExtension(media.Source); + + image.Mutate(x => x.Resize(resizeOptions)); + + string thumbFileName = media.Name.TrimEnd(extension) + extensionPrefix + extension; + image.Save(Path.Combine(Paths.Media, media.FileId, thumbFileName)); + return Path.Combine(PATH_PREFIX, media.FileId, thumbFileName).Replace(Path.DirectorySeparatorChar, PATH_SEPARATOR); + } + + + /// + /// Create image data if available + /// MediaImageMeta GetImageMeta(Image image) { var pngMetadata = image.Metadata.GetPngMetadata(); @@ -115,7 +127,7 @@ namespace zero.Core.Api DPI = image.Metadata.HorizontalResolution, ColorSpace = image.Metadata.IccProfile?.Header?.DataColorSpace.ToString(), HasTransparency = pngMetadata?.HasTransparency ?? false, - IsAnimated = image.Frames.Count > 1 + Frames = image.Frames.Count }; } } diff --git a/zero.Core/Entities/Media/MediaImageMeta.cs b/zero.Core/Entities/Media/MediaImageMeta.cs index fd9517f7..e0558dcd 100644 --- a/zero.Core/Entities/Media/MediaImageMeta.cs +++ b/zero.Core/Entities/Media/MediaImageMeta.cs @@ -38,8 +38,8 @@ namespace zero.Core.Entities public bool HasTransparency { get; set; } /// - /// Whether this image contains multiple frames + /// How many frames contains this image (for animation) /// - public bool IsAnimated { get; set; } + public int Frames { get; set; } = 1; } } diff --git a/zero.Web.UI/App/editor/editor-component.vue b/zero.Web.UI/App/editor/editor-component.vue index 4caa5cf1..5c4b772b 100644 --- a/zero.Web.UI/App/editor/editor-component.vue +++ b/zero.Web.UI/App/editor/editor-component.vue @@ -208,7 +208,14 @@ onChange(value) { - Objects.setValue(this.value, this.selector, value); + if (typeof value === 'function') + { + value(this.value); + } + else + { + Objects.setValue(this.value, this.selector, value); + } this.$emit('input', this.value); } } diff --git a/zero.Web.UI/App/pages/media/detail.vue b/zero.Web.UI/App/pages/media/detail.vue index c1456a97..e5701d7f 100644 --- a/zero.Web.UI/App/pages/media/detail.vue +++ b/zero.Web.UI/App/pages/media/detail.vue @@ -21,6 +21,9 @@ {{model.imageMeta.colorSpace}} + + {{model.imageMeta.frames}} + diff --git a/zero.Web.UI/App/pages/media/upload.vue b/zero.Web.UI/App/pages/media/upload.vue new file mode 100644 index 00000000..c9ceb124 --- /dev/null +++ b/zero.Web.UI/App/pages/media/upload.vue @@ -0,0 +1,151 @@ + + + + + + \ No newline at end of file diff --git a/zero.Web.UI/App/renderers/media.js b/zero.Web.UI/App/renderers/media.js index ba717f81..928786d2 100644 --- a/zero.Web.UI/App/renderers/media.js +++ b/zero.Web.UI/App/renderers/media.js @@ -12,6 +12,12 @@ }, fields: [ + { + field: 'source', + display: 'custom', + required: true, + path: '@zero/pages/media/upload.vue' + }, { field: 'name', display: 'text', diff --git a/zero.Web.UI/App/resources/media.js b/zero.Web.UI/App/resources/media.js index 7597b67a..752d5254 100644 --- a/zero.Web.UI/App/resources/media.js +++ b/zero.Web.UI/App/resources/media.js @@ -45,13 +45,13 @@ export default { }, // uploads a file - upload(file, folderId, onProgress) + upload(file, folderId, onProgress, isTemporary) { var data = new FormData(); data.append('file', file); data.append('folderId', folderId); - return Axios.post(base + 'upload', data, { + return Axios.post(base + (isTemporary ? 'uploadTemporary' : 'upload'), data, { onUploadProgress: (progressEvent) => { if (typeof onProgress === 'function') diff --git a/zero.Web/Controllers/MediaController.cs b/zero.Web/Controllers/MediaController.cs index 6c1a2e43..c85b2268 100644 --- a/zero.Web/Controllers/MediaController.cs +++ b/zero.Web/Controllers/MediaController.cs @@ -78,10 +78,9 @@ namespace zero.Web.Controllers /// /// Save a media item /// - public async Task Save([FromBody] MediaEditModel model) + public async Task Save([FromBody] Media model) { - Media entity = await Mapper.Map(model, await Api.GetById(model.Id)); - return await As(await Api.Save(entity)); + return Json(await Api.Save(model)); } @@ -95,6 +94,16 @@ namespace zero.Web.Controllers } + /// + /// Upload a file + /// + public async Task UploadTemporary(IFormFile file, string folderId) + { + Media media = await MediaUploadApi.Upload(file, folderId); + return Json(media); + } + + /// /// Deletes a media item /// diff --git a/zero.Web/Resources/Localization/zero.en-us.json b/zero.Web/Resources/Localization/zero.en-us.json index b6e35dc3..c53350dd 100644 --- a/zero.Web/Resources/Localization/zero.en-us.json +++ b/zero.Web/Resources/Localization/zero.en-us.json @@ -448,12 +448,13 @@ "size": "Size", "date": "Last modified at", "filename": "Filename", + "source": "File", "alternativeText": "Alternative text", "alternativeText_text": "Text which is used when the image can't be loaded", "dpi": "DPI", "colorSpace": "Color space", "hasTransparency": "Has transparency", - "isAnimated": "Is animated" + "frames": "Frames" } },