System.Drawing в ASP.NET ASP.NET Core

Есть много вопросов, связанных с использованием библиотеки System.Drawing в сервисах ASP.NET. Самый распространенный ответ заключается в том, что Microsoft не рекомендует использовать System.Drawing в службах ASP.NET.

В этой статье вы узнаете подробности об использовании интерфейса библиотеки System.Drawing в службах ASP.NET с помощью Aspose.Drawing for .NET. В следующих разделах структурирована вся необходимая информация:

Почему Aspose.Drawing?

Microsoft не рекомендует использовать свою библиотеку System.Drawing в службах ASP.NET. Домашняя страница System.Drawing документация гласит:

Классы в пространстве имен System.Drawing не поддерживаются для использования в службах Windows или ASP.NET. Попытка использовать эти классы из одного из этих типов приложений может привести к возникновению исключений во время выполнения и снижению производительности службы.

Есть две основные проблемы, которые заставляют Microsoft писать выше осторожности. Первая проблема — это использование нативной библиотеки GDI+ и, как следствие, использование дескрипторов GDI. В то время как вторая проблема связана с параллелизмом. Например, блокировка всего процесса происходит во время любой операции DrawImage(). Поэтому нельзя создавать быстрые и масштабируемые сервисы ASP.NET с помощью библиотеки System.Drawing от Microsoft.

Установка Aspose.Drawing для .NET API

Вы можете легко загрузить файл DLL из раздела Загрузки или настроить его с помощью диспетчера пакетов NuGet с помощью следующей команды:

PM> Install-Package Aspose.Drawing

Aspose.Drawing для .NET API — Введение

Мы понимаем, что System.Drawing очень популярен среди разработчиков из-за своих возможностей обработки изображений. Вот почему мы создали аналогичный и совместимый API без каких-либо проблем с совместимостью. Для вашего удобства и адаптивности API содержит одинаковые имена классов, функций, перечислений и интерфейсов. Вы можете просто изменить ссылку на проект с System.Drawing на Aspose.Drawing и перекомпилировать программу.

Более того, Aspose.Drawing — это управляемая библиотека, доступная для NET Framework 2.0 и NET Core 2.0. В отличие от System.Drawing, он не зависит от какой-либо платформы, такой как Linux, Azure и т. д.

Использование Aspose.Drawing в службах ASP.NET

Реализация Aspose.Drawing не зависит от GDI или GDI+. Он не использует дескрипторы GDI и редко использует блокировки процесса.

Демонстрация использования Aspose.Drawing в службах ASP.NET

Давайте создадим простое приложение, объясняющее преимущества использования библиотеки Aspose.Drawing:

Представьте, что вы решили создать совершенно новый веб-сервис или приложение. Приложение сгенерирует набор миниатюр с прикрепленными к ним фильтрами из изображения пользователя.

Вы можете начать с введения нескольких новых объектов:

///<summary>
/// Доступные типы фильтров.
///</summary>
public enum FilterType
{
    ///<summary>
    /// Фильтр для генерации изображения, насыщенного красным цветом.
    ///</summary>
    Red,

    ///<summary>
    /// Фильтр для генерации изображения, насыщенного зеленым цветом.
    ///</summary>
    Green,

    ///<summary>
    /// Фильтр для генерации изображения, насыщенного синим цветом.
    ///</summary>
    Blue,

    ///<summary>
    /// Фильтр для генерации изображения в градациях серого.
    ///</summary>
    Grayscale,

    ///<summary>
    /// Фильтр для генерации негатива изображения.
    ///</summary>
    Negative,

    ///<summary>
    /// Фильтр для создания изображения сепии.
    ///</summary>
    Sepia,
}
///<summary>
/// Результат генерации отфильтрованных эскизов.
///</summary>
public class FilterThumbnailResult
{
    ///<summary>
    /// Инициализирует новый экземпляр класса FilterThumbnailResult.
    ///</summary>
    ///<param name="filterType"> Тип фильтра, примененного к миниатюре.</param>
    ///<param name="pngData"> Данные изображения в формате PNG.</param>
    ///<param name="width"> Ширина миниатюры.</param>
    ///<param name="height"> Высота миниатюры.</param>
    public FilterThumbnailResult(FilterType filterType, byte[] pngData, int width, int height)
    {
        this.FilterType = filterType;
        this.PngData = pngData;
        this.Width = width;
        this.Height = height;
    }

    ///<summary>
    /// Получает тип фильтра, примененного к миниатюре.
    ///</summary>
    public FilterType FilterType { get; }

    ///<summary>
    /// Получает данные изображения в формате PNG.
    ///</summary>
    public byte[] PngData { get; }

    ///<summary>
    /// Получает ширину эскиза.
    ///</summary>
    public int Width { get; }

    ///<summary>
    /// Получает высоту миниатюры.
    ///</summary>
    public int Height { get; }
}

Теперь вы можете определить интерфейс для создания эскизов с прикрепленными фильтрами:

using System.Collections.Generic;

///<summary>
/// Интерфейс для создания эскизов изображений.
///</summary>
public interface IThumbnailGenerator
{
    ///<summary>
    /// Генерирует набор миниатюр для всех доступных фильтров.
    ///</summary>
    ///<param name="sourceImage"> Байты исходного изображения.</param>
    ///<param name="width"> Требуемая ширина изображений предварительного просмотра.</param>
    ///<param name="height"> Требуемая высота изображений для предварительного просмотра.</param>
    ///<returns> Набор миниатюр.</returns>
    IEnumerable<FilterThumbnailResult> GenerateAllThumbnails(byte[] sourceImage, int width, int height);
}

Переходя к реализации, предположим реализацию интерфейса IThumbnailGenerator с использованием библиотеки System.Drawing. Это будет выглядеть как следующий фрагмент кода:

///<summary>
/// Реализация интерфейса IThumbnailGenerator с использованием System.Drawing.
///</summary>
public class ThumbnailGenerator
    : IThumbnailGenerator
{
    ///<summary>
    /// Набор обработчиков эскизов.
    ///</summary>
    private static readonly FilterThumbnailProcessor[] Processors = new FilterThumbnailProcessor[] 
    {
        ApplyRedFilter,
        ApplyGreenFilter,
        ApplyBlueFilter,
        ApplyGrayFilter,
        ApplyNegativeFilter,
        ApplySepiaFilter,
    };

    ///<summary>
    /// Делегат для обработки эскизов.
    ///</summary>
    ///<param name="thumbnail"> Миниатюра исходного изображения.</param>
    ///<returns> Результат создания отфильтрованных эскизов.</returns>
    private delegate FilterThumbnailResult FilterThumbnailProcessor(Bitmap thumbnail);

    ///<summary>
    /// Генерирует набор миниатюр для всех доступных фильтров.
    ///</summary>
    ///<param name="sourceImage"> Байты исходного изображения.</param>
    ///<param name="width"> Требуемая ширина изображений предварительного просмотра.</param>
    ///<param name="height"> Требуемая высота изображений для предварительного просмотра.</param>
    ///<returns> Набор миниатюр.</returns>
    public IEnumerable<FilterThumbnailResult> GenerateAllThumbnails(byte[] sourceImage, int width, int height)
    {
        using (MemoryStream ms = new MemoryStream(sourceImage))
        using (Bitmap bitmap = new Bitmap(ms))
        {
            foreach (var processor in Processors)
            {
                using (Bitmap previewBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
                using (Graphics graphics = Graphics.FromImage(previewBitmap))
                {
                    graphics.DrawImage(bitmap, 0, 0, width, height);

                    yield return processor(previewBitmap);
                }
            }
        }
    }

    ///<summary>
    /// Применяет красный фильтр к эскизу.
    ///</summary>
    ///<param name="thumbnail"> Миниатюра исходного изображения.</param>
    ///<returns> Результат создания отфильтрованных эскизов.</returns>
    private static FilterThumbnailResult ApplyRedFilter(Bitmap thumbnail)
    {
    ...
    }

    ///<summary>
    /// Применяет зеленый фильтр к эскизу.
    ///</summary>
    ///<param name="thumbnail"> Миниатюра исходного изображения.</param>
    ///<returns> Результат создания отфильтрованных эскизов.</returns>
    private static FilterThumbnailResult ApplyGreenFilter(Bitmap thumbnail)
    {
    ...
    }

    ///<summary>
    /// Применяет синий фильтр к эскизу.
    ///</summary>
    ///<param name="thumbnail"> Миниатюра исходного изображения.</param>
    ///<returns> Результат создания отфильтрованных эскизов.</returns>
    private static FilterThumbnailResult ApplyBlueFilter(Bitmap thumbnail)
    {
    ...
    }

    ///<summary>
    /// Применяет серый фильтр к эскизу.
    ///</summary>
    ///<param name="thumbnail"> Миниатюра исходного изображения.</param>
    ///<returns> Результат создания отфильтрованных эскизов.</returns>
    private static FilterThumbnailResult ApplyGrayFilter(Bitmap thumbnail)
    {
    ...
    }

    ///<summary>
    /// Применяет отрицательный фильтр к эскизу.
    ///</summary>
    ///<param name="thumbnail"> Миниатюра исходного изображения.</param>
    ///<returns> Результат создания отфильтрованных эскизов.</returns>
    private static FilterThumbnailResult ApplyNegativeFilter(Bitmap thumbnail)
    {
    ...
    }

    ///<summary>
    /// Применение фильтра сепия к эскизу.
    ///</summary>
    ///<param name="thumbnail"> Миниатюра исходного изображения.</param>
    ///<returns> Результат создания отфильтрованных эскизов.</returns>
    private static FilterThumbnailResult ApplySepiaFilter(Bitmap thumbnail)
    {
    ...
    }
}

Код будет работать нормально, но у него есть один недостаток — он генерирует все эскизы последовательно. Библиотека System.Drawing основана на довольно старой библиотеке GDI+, которая была разработана как однопоточная библиотека, поэтому она может тратить ресурсы при наличии нескольких ядер ЦП.

Использование Aspose.Drawing может помочь значительно повысить производительность, поскольку оно не имеет ограничений, связанных с одним потоком. Пожалуйста, попробуйте изменить только одну функцию:

///<summary>
/// Генерирует набор миниатюр для всех доступных фильтров.
///</summary>
///<param name="sourceImage"> Байты исходного изображения.</param>
///<param name="width"> Требуемая ширина изображений предварительного просмотра.</param>
///<param name="height"> Требуемая высота изображений для предварительного просмотра.</param>
///<returns> Набор миниатюр.</returns>
public IEnumerable<FilterThumbnailResult> GenerateAllThumbnails(byte[] sourceImage, int width, int height)
{
    using (MemoryStream ms = new MemoryStream(sourceImage))
    using (Bitmap bitmap = new Bitmap(ms))
    {
        ConcurrentBag<FilterThumbnailResult> collection = new ConcurrentBag<FilterThumbnailResult>();

        Parallel.ForEach(Processors, (processor) =>
        {
            using (Bitmap previewBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
            using (Graphics graphics = Graphics.FromImage(previewBitmap))
            {
                graphics.DrawImage(bitmap, 0, 0, width, height);
                collection.Add(processor(previewBitmap));
            }
        });

        return collection;
    }
}

Этот код теперь может использовать несколько ядер ЦП без каких-либо существенных изменений исходного исходного кода. Замена System.Drawing на Aspose.Drawing повышает производительность ваших приложений и служб.

Скачать исходный код

Вы можете скачать Исходный код, относящийся к этому примеру, для ознакомления.

Бесплатная пробная лицензия API

Вы можете запросить Бесплатную временную лицензию, чтобы полностью протестировать API.

Вывод

В заключение вы узнали на примере, насколько просто использовать библиотеку Aspose.Drawing в ваших службах ASP.NET и ASP.NET Core. Более того, Aspose.Drawing не имеет проблем или внешних зависимостей, существующих в System.Drawing. Кроме того, вы можете подробно изучить API, просмотрев Документацию. Пожалуйста, не стесняйтесь обращаться к нам в любое время через Бесплатный форум поддержки по любым вопросам!

Смотрите также