Merge pull request #10964 from umbraco/v9/bugfix/imagesize-cleanup

Use ImageSharps Image.Identify for dimension extraction
This commit is contained in:
Mole
2021-09-09 12:29:17 +02:00
committed by GitHub
8 changed files with 72 additions and 126 deletions

View File

@@ -1,26 +0,0 @@
using System;
using System.IO;
using Umbraco.Cms.Core.Media.Exif;
namespace Umbraco.Cms.Core.Media
{
public static class ExifImageDimensionExtractor
{
public static bool TryGetDimensions(Stream stream, out int width, out int height)
{
var jpgInfo = ImageFile.FromStream(stream);
height = -1;
width = -1;
if (jpgInfo != null
&& jpgInfo.Format != ImageFileFormat.Unknown
&& jpgInfo.Properties.ContainsKey(ExifTag.PixelYDimension)
&& jpgInfo.Properties.ContainsKey(ExifTag.PixelXDimension))
{
height = Convert.ToInt32(jpgInfo.Properties[ExifTag.PixelYDimension].Value);
width = Convert.ToInt32(jpgInfo.Properties[ExifTag.PixelXDimension].Value);
}
return height > 0 && width > 0;
}
}
}

View File

@@ -1,9 +1,10 @@
using System.IO;
using System.Drawing;
using System.IO;
namespace Umbraco.Cms.Core.Media
{
public interface IImageDimensionExtractor
{
public ImageSize GetDimensions(Stream stream);
public Size? GetDimensions(Stream stream);
}
}

View File

@@ -1,15 +0,0 @@
namespace Umbraco.Cms.Core.Media
{
public struct ImageSize
{
public int Width { get; }
public int Height { get; }
public ImageSize(int width, int height)
{
Width = width;
Height = height;
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Media;
namespace Umbraco.Extensions
@@ -8,15 +9,21 @@ namespace Umbraco.Extensions
/// <summary>
/// Gets a value indicating whether the file extension corresponds to a supported image.
/// </summary>
/// <param name="imageUrlGenerator">The image URL generator implementation that provides detail on which image extension sare supported.</param>
/// <param name="imageUrlGenerator">The image URL generator implementation that provides detail on which image extensions are supported.</param>
/// <param name="extension">The file extension.</param>
/// <returns>A value indicating whether the file extension corresponds to an image.</returns>
/// <returns>
/// A value indicating whether the file extension corresponds to an image.
/// </returns>
/// <exception cref="System.ArgumentNullException">imageUrlGenerator</exception>
public static bool IsSupportedImageFormat(this IImageUrlGenerator imageUrlGenerator, string extension)
{
if (imageUrlGenerator == null) throw new ArgumentNullException(nameof(imageUrlGenerator));
if (extension == null) return false;
extension = extension.TrimStart('.');
return imageUrlGenerator.SupportedImageFileTypes.InvariantContains(extension);
if (imageUrlGenerator == null)
{
throw new ArgumentNullException(nameof(imageUrlGenerator));
}
return string.IsNullOrWhiteSpace(extension) == false &&
imageUrlGenerator.SupportedImageFileTypes.InvariantContains(extension.TrimStart(Constants.CharArrays.Period));
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Drawing;
using System.IO;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Configuration.Models;
@@ -74,9 +75,7 @@ namespace Umbraco.Cms.Core.Media
{
using (var filestream = _mediaFileManager.FileSystem.OpenFile(filepath))
{
var extension = (Path.GetExtension(filepath) ?? "").TrimStart(Constants.CharArrays.Period);
var size = _imageUrlGenerator.IsSupportedImageFormat(extension) ? (ImageSize?)_imageDimensionExtractor.GetDimensions(filestream) : null;
SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment);
SetProperties(content, autoFillConfig, filepath, filestream, culture, segment);
}
}
catch (Exception ex)
@@ -109,13 +108,22 @@ namespace Umbraco.Cms.Core.Media
}
else
{
var extension = (Path.GetExtension(filepath) ?? "").TrimStart(Constants.CharArrays.Period);
var size = _imageUrlGenerator.IsSupportedImageFormat(extension) ? (ImageSize?)_imageDimensionExtractor.GetDimensions(filestream) : null;
SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment);
SetProperties(content, autoFillConfig, filepath, filestream, culture, segment);
}
}
private static void SetProperties(IContentBase content, ImagingAutoFillUploadField autoFillConfig, ImageSize? size, long length, string extension, string culture, string segment)
private void SetProperties(IContentBase content, ImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream, string culture, string segment)
{
var extension = (Path.GetExtension(filepath) ?? string.Empty).TrimStart(Constants.CharArrays.Period);
var size = _imageUrlGenerator.IsSupportedImageFormat(extension)
? _imageDimensionExtractor.GetDimensions(filestream) ?? (Size?)new Size(Constants.Conventions.Media.DefaultSize, Constants.Conventions.Media.DefaultSize)
: null;
SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment);
}
private static void SetProperties(IContentBase content, ImagingAutoFillUploadField autoFillConfig, Size? size, long length, string extension, string culture, string segment)
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig));

View File

@@ -189,7 +189,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
// Add default ImageSharp configuration and service implementations
builder.Services.AddSingleton(SixLabors.ImageSharp.Configuration.Default);
builder.Services.AddSingleton<IImageDimensionExtractor, ImageDimensionExtractor>();
builder.Services.AddSingleton<IImageDimensionExtractor, ImageSharpDimensionExtractor>();
builder.Services.AddSingleton<IImageUrlGenerator, ImageSharpImageUrlGenerator>();
builder.Services.AddSingleton<PackageDataInstallation>();

View File

@@ -1,68 +0,0 @@
using System;
using System.IO;
using SixLabors.ImageSharp;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Media;
namespace Umbraco.Cms.Infrastructure.Media
{
internal class ImageDimensionExtractor : IImageDimensionExtractor
{
/// <summary>
/// The ImageSharp configuration.
/// </summary>
private readonly Configuration _configuration;
/// <summary>
/// Initializes a new instance of the <see cref="ImageDimensionExtractor" /> class.
/// </summary>
/// <param name="configuration">The ImageSharp configuration.</param>
public ImageDimensionExtractor(Configuration configuration) => _configuration = configuration;
/// <summary>
/// Gets the dimensions of an image.
/// </summary>
/// <param name="stream">A stream containing the image bytes.</param>
/// <returns>The dimension of the image.</returns>
/// <remarks>First try with EXIF as it is faster and does not load the entire image
/// in memory. Fallback to GDI which means loading the image in memory and thus
/// use potentially large amounts of memory.</remarks>
public ImageSize GetDimensions(Stream stream)
{
// Try to load with exif
try
{
if (ExifImageDimensionExtractor.TryGetDimensions(stream, out var width, out var height))
{
return new ImageSize(width, height);
}
}
catch
{
// We will just swallow, just means we can't read exif data, we don't want to log an error either
}
// we have no choice but to try to read in via GDI
try
{
if (stream.CanRead && stream.CanSeek)
{
stream.Seek(0, SeekOrigin.Begin);
}
using (var image = Image.Load(_configuration, stream))
{
var fileWidth = image.Width;
var fileHeight = image.Height;
return new ImageSize(fileWidth, fileHeight);
}
}
catch (Exception)
{
// We will just swallow, just means we can't read via GDI, we don't want to log an error either
}
return new ImageSize(Constants.Conventions.Media.DefaultSize, Constants.Conventions.Media.DefaultSize);
}
}
}

View File

@@ -0,0 +1,39 @@
using System.IO;
using SixLabors.ImageSharp;
using Umbraco.Cms.Core.Media;
using Size = System.Drawing.Size;
namespace Umbraco.Cms.Infrastructure.Media
{
internal class ImageSharpDimensionExtractor : IImageDimensionExtractor
{
private readonly Configuration _configuration;
/// <summary>
/// Initializes a new instance of the <see cref="ImageSharpDimensionExtractor" /> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
public ImageSharpDimensionExtractor(Configuration configuration)
=> _configuration = configuration;
/// <summary>
/// Gets the dimensions of an image.
/// </summary>
/// <param name="stream">A stream containing the image bytes.</param>
/// <returns>
/// The dimension of the image.
/// </returns>
public Size? GetDimensions(Stream stream)
{
Size? size = null;
IImageInfo imageInfo = Image.Identify(_configuration, stream);
if (imageInfo != null)
{
size = new Size(imageInfo.Width, imageInfo.Height);
}
return size;
}
}
}