From 657fb589137099794e58fbd35beb7d942b376965 Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 25 Apr 2021 21:20:04 +0800 Subject: ... --- .../Timeline/Services/Imaging/ImageException.cs | 58 +++++++++ .../Timeline/Services/Imaging/ImageValidator.cs | 53 ++++++++ .../Timeline/Services/Imaging/Resource.Designer.cs | 108 +++++++++++++++++ BackEnd/Timeline/Services/Imaging/Resource.resx | 135 +++++++++++++++++++++ 4 files changed, 354 insertions(+) create mode 100644 BackEnd/Timeline/Services/Imaging/ImageException.cs create mode 100644 BackEnd/Timeline/Services/Imaging/ImageValidator.cs create mode 100644 BackEnd/Timeline/Services/Imaging/Resource.Designer.cs create mode 100644 BackEnd/Timeline/Services/Imaging/Resource.resx (limited to 'BackEnd/Timeline/Services/Imaging') diff --git a/BackEnd/Timeline/Services/Imaging/ImageException.cs b/BackEnd/Timeline/Services/Imaging/ImageException.cs new file mode 100644 index 00000000..926ecc0a --- /dev/null +++ b/BackEnd/Timeline/Services/Imaging/ImageException.cs @@ -0,0 +1,58 @@ +using System; +using System.Globalization; + +namespace Timeline.Services.Imaging +{ + [Serializable] + public class ImageException : Exception + { + public enum ErrorReason + { + /// + /// Decoding image failed. + /// + CantDecode, + /// + /// Decoding succeeded but the real type is not the specified type. + /// + UnmatchedFormat, + /// + /// Image is not of required size. + /// + NotSquare, + /// + /// Other unknown errer. + /// + Unknown + } + + public ImageException() : this(null) { } + public ImageException(string? message) : this(message, null) { } + public ImageException(string? message, Exception? inner) : this(ErrorReason.Unknown, null, null, null, message, inner) { } + + public ImageException(ErrorReason error, byte[]? data, string? requestType, string? realType, Exception? inner) : this(error, data, requestType, realType, null, inner) { } + public ImageException(ErrorReason error, byte[]? data, string? requestType = null, string? realType = null, string? message = null, Exception? inner = null) : base(message ?? MakeMessage(error), inner) { Error = error; ImageData = data; RequestType = requestType; RealType = realType; } + + protected ImageException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + + private static string MakeMessage(ErrorReason? reason) => + string.Format(CultureInfo.InvariantCulture, Resource.ExceptionImage, reason switch + { + ErrorReason.CantDecode => Resource.ExceptionImageReasonCantDecode, + ErrorReason.UnmatchedFormat => Resource.ExceptionImageReasonUnmatchedFormat, + ErrorReason.NotSquare => Resource.ExceptionImageReasonBadSize, + _ => Resource.ExceptionImageReasonUnknownError + }); + + public ErrorReason Error { get; } +#pragma warning disable CA1819 // Properties should not return arrays + public byte[]? ImageData { get; } +#pragma warning restore CA1819 // Properties should not return arrays + public string? RequestType { get; } + + // This field will be null if decoding failed. + public string? RealType { get; } + } +} diff --git a/BackEnd/Timeline/Services/Imaging/ImageValidator.cs b/BackEnd/Timeline/Services/Imaging/ImageValidator.cs new file mode 100644 index 00000000..b4ae68dc --- /dev/null +++ b/BackEnd/Timeline/Services/Imaging/ImageValidator.cs @@ -0,0 +1,53 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Timeline.Services.Imaging +{ + public interface IImageValidator + { + /// + /// Validate a image data. + /// + /// The data of the image. Can't be null. + /// If not null, the real image format will be check against the requested format and throw if not match. If null, then do not check. + /// If true, image must be square. + /// The format. + /// Thrown when is null. + /// Thrown when image data can't be decoded or real type does not match request type or image is not square when required. + Task Validate(byte[] data, string? requestType = null, bool square = false); + } + + public class ImageValidator : IImageValidator + { + public ImageValidator() + { + } + + public async Task Validate(byte[] data, string? requestType = null, bool square = false) + { + if (data == null) + throw new ArgumentNullException(nameof(data)); + + var format = await Task.Run(() => + { + try + { + using var image = Image.Load(data, out IImageFormat format); + if (requestType != null && !format.MimeTypes.Contains(requestType)) + throw new ImageException(ImageException.ErrorReason.UnmatchedFormat, data, requestType, format.DefaultMimeType); + if (square && image.Width != image.Height) + throw new ImageException(ImageException.ErrorReason.NotSquare, data, requestType, format.DefaultMimeType); + return format; + } + catch (UnknownImageFormatException e) + { + throw new ImageException(ImageException.ErrorReason.CantDecode, data, requestType, null, null, e); + } + }); + return format; + } + } +} diff --git a/BackEnd/Timeline/Services/Imaging/Resource.Designer.cs b/BackEnd/Timeline/Services/Imaging/Resource.Designer.cs new file mode 100644 index 00000000..e9218208 --- /dev/null +++ b/BackEnd/Timeline/Services/Imaging/Resource.Designer.cs @@ -0,0 +1,108 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Timeline.Services.Imaging { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Services.Imaging.Resource", typeof(Resource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Image is in valid because {0}.. + /// + internal static string ExceptionImage { + get { + return ResourceManager.GetString("ExceptionImage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to image is not of required size. + /// + internal static string ExceptionImageReasonBadSize { + get { + return ResourceManager.GetString("ExceptionImageReasonBadSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to failed to decode image, see inner exception. + /// + internal static string ExceptionImageReasonCantDecode { + get { + return ResourceManager.GetString("ExceptionImageReasonCantDecode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to unknown error. + /// + internal static string ExceptionImageReasonUnknownError { + get { + return ResourceManager.GetString("ExceptionImageReasonUnknownError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to image's actual mime type is not the specified one. + /// + internal static string ExceptionImageReasonUnmatchedFormat { + get { + return ResourceManager.GetString("ExceptionImageReasonUnmatchedFormat", resourceCulture); + } + } + } +} diff --git a/BackEnd/Timeline/Services/Imaging/Resource.resx b/BackEnd/Timeline/Services/Imaging/Resource.resx new file mode 100644 index 00000000..060e2a91 --- /dev/null +++ b/BackEnd/Timeline/Services/Imaging/Resource.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Image is in valid because {0}. + + + image is not of required size + + + failed to decode image, see inner exception + + + unknown error + + + image's actual mime type is not the specified one + + \ No newline at end of file -- cgit v1.2.3