System.Drawing.Image FromStream непоследовательно возвращает исключение из нехватки памяти
Я пытаюсь улучшить функцию загрузки изображений наших веб-приложений, которая сохраняет изображения в базе данных в виде байтового массива, а затем считывает их позже и помещает в тег изображения HTML для отображения.
Чтобы отобразить все загруженные изображения, у нас есть отдельный набор методов для извлечения миниатюрного изображения, которое включает чтение из базы данных, преобразование в поток памяти и последующее использование этого для создания C# -образа, за которым следует метод.GetThumbnail перед преобразованием. он возвращается в байтовый массив через другой объект потока памяти.
Сетка загружает все данные (имя изображения, описание, категория и т. Д.), А затем вызывает отдельный URL-адрес с идентификатором изображения для получения эскиза изображения. Этот URL возвращает C# MVC ImageResult. Когда я вызываю этот URL сам по себе, он загружает правильный эскиз без проблем. Однако, когда я вызываю сетку, она прекрасно загружает другие изображения, а затем падает с исключением из-за нехватки памяти. Если я пропущу это, он продолжит загружать и другие изображения.
Сначала я подумал, что это может быть связано с тем, что один из потоков оставлен открытым, но все заключено в использование с помощью Dispose() и Close(), вызываемых в обоих потоках памяти (первый для преобразования его в изображение, второй для его преобразования). обратно в байтовый массив) в блоках finally.
У меня совершенно нет идей, поскольку похоже, что байтовый массив одинаков, вызываемый метод одинаков, но в одном случае он работает, а в другом - нет.
Я скопировал вредоносный код, который является методом, который преобразует изображение в миниатюру через наш объект Image (переданный в качестве 4-х параметров), последовательно падает на строку System.Drawing.Image.FromStream (ms) line.
private static void ConvertToImage(int size, bool fixWidth, bool fixHeight, Image img)
{
byte[] picbyte = img.Img;
using (MemoryStream ms = new MemoryStream(picbyte))
{
try
{
System.Drawing.Image image = System.Drawing.Image.FromStream(ms);
int width = image.Width;
int height = image.Height;
if (fixWidth && !fixHeight)
{
height = (int)Math.Round(((decimal)height / width) * size);
width = size;
}
if (fixHeight && !fixWidth)
{
width = (int)Math.Round(((decimal)width / height) * size);
height = size;
}
if ((fixWidth && fixHeight) || (!fixWidth && !fixHeight))
{
width = size;
height = size;
}
IntPtr ptr = Marshal.AllocHGlobal(sizeof(int));
int ptrInt = 0;
Marshal.WriteInt32(ptr, ptrInt);
Marshal.FreeHGlobal(ptr);
MemoryStream ms2 = new MemoryStream();
using (image = image.GetThumbnailImage(width, height, delegate () { return false; }, ptr))
{
try
{
image.Save(ms2, System.Drawing.Imaging.ImageFormat.Png);
img.Img = ms2.ToArray();
img.MIMEType = "image/png";
image.Dispose();
}
finally
{
ms2.Close();
ms2.Dispose();
}
}
}
finally //Ensure we close the stream if anything happens.
{
ms.Close();
ms.Dispose();
}
}
}
1 ответ
GDI (вещь классы, как Image
а также Bitmap
являются обертками для) OutOfMemoryExecption
когда лучшим исключением было бы имя с лучшим именем, не возбуждая, OutOfHandlesException
,
При работе с изображениями в.NET вы ДОЛЖНЫ всегда распоряжаться своими ресурсами, объекты, с которыми вы работаете, часто являются классами, которые не занимают много управляемой памяти, но держат ограниченные неуправляемые ресурсы. Поскольку они не оказывают большого давления памяти на сборщик мусора, если вы создаете много из них, вы можете легко исчерпать дескрипторы GDI до того, как GC запустит и соберет их.
В верхней части вашей функции вы делаете
System.Drawing.Image image = System.Drawing.Image.FromStream(ms);
потом ты сделаешь
using (image = image.GetThumbnailImage(width, height, delegate () { return false; }, ptr))
Это заставляет вас потерять ссылку на первый Image
объект, не выбрасывая его. Используйте другое имя переменной для вашего эскиза и поместите это первое изображение в using
блок.
PS Ваш .Close();
а также .Dispose()
звонки лишние. Помещение одноразовых предметов внутри using
блок выполняет обе эти операции, вы можете избавиться от всех ваших блоков try-finally и избавиться от лишних image.Dispose()
вызов. Также ваш ptr
не правильно, MSDN утверждает, что вы должны передать IntPtr.Zero
не значение 0, записанное в указатель.
Вот быстро обновленная версия, в которой есть все исправления.
private static void ConvertToImage(int size, bool fixWidth, bool fixHeight, Image img)
{
byte[] picbyte = img.Img;
using (MemoryStream ms = new MemoryStream(picbyte))
using (System.Drawing.Image image = System.Drawing.Image.FromStream(ms))
{
int width = image.Width;
int height = image.Height;
if (fixWidth && !fixHeight)
{
height = (int)Math.Round(((decimal)height / width) * size);
width = size;
}
if (fixHeight && !fixWidth)
{
width = (int)Math.Round(((decimal)width / height) * size);
height = size;
}
if ((fixWidth && fixHeight) || (!fixWidth && !fixHeight))
{
width = size;
height = size;
}
using(MemoryStream ms2 = new MemoryStream())
using (var thumnailImage = image.GetThumbnailImage(width, height, delegate () { return false; }, IntPtr.Zero))
{
thumnailImage.Save(ms2, System.Drawing.Imaging.ImageFormat.Png);
img.Img = ms2.ToArray();
img.MIMEType = "image/png";
}
}
}