Как написать метод расширения, который преобразует System.Drawing.Bitmap в массив байтов? Почему бы и нет:
<Extension()> _
Public Function ToByteArray(ByVal image As System.Drawing.Bitmap) As Byte()
Using ms = New MemoryStream()
image.Save(ms, image.RawFormat)
Return ms.ToArray()
End Using
End Function
Тем не менее, когда я использую это, я получаю "System.Runtime.InteropServices.ExternalException: общая ошибка в GDI +", выведенная из операции Save(). Что я делаю неправильно?
Ответ 1
Как утверждает кто-то другой, это известная ошибка GDI +.
Однако обычно это появляется, когда вы закрыли исходный поток изображения, прежде чем полностью его прочитать. Просто загрузка нового объекта изображения будет загружать только метаданные, такие как ширина, высота, глубина цвета и т.д., А не фактические пиксели. Они будут лениво загружены позднее.
Этого можно избежать, копируя изображение (во время загрузки) в новое изображение, созданное в памяти. Я предполагаю, что входной поток все еще доступен в это время. После того, как вы приобретете новый класс изображений на основе памяти, вы можете свободно распоряжаться источником. (Другим решением было бы не закрывать/удалять исходный поток).
Изменить:
Проблема описана в KB814675 зависимостях Bitmap и Image constructor вместе с обходным решением.
Создать неиндексированное изображение
Этот подход требует, чтобы новое изображение находилось в неиндексированном пиксельном формате (более 8 бит на пиксель), даже если исходное изображение было в индексированном формате. В этом обходном пути используется метод Graphics.DrawImage() для копирования изображения в новый объект Bitmap:
- Построить исходное растровое изображение из потока, из памяти или из файла.
- Создайте новый Bitmap того же размера, с пиксельным форматом более 8 бит на пиксель (BPP).
- Используйте метод Graphics.FromImage() для получения объекта Graphics для второго растрового изображения.
- Используйте Graphics.DrawImage(), чтобы нарисовать первый битмап на втором растровом изображении.
- Используйте Graphics.Dispose() для удаления графики.
- Используйте Bitmap.Dispose(), чтобы избавиться от первого растрового изображения.
Создать индексированное изображение
В этом обходном пути создается объект Bitmap в индексированном формате:
- Построить исходное растровое изображение из потока, из памяти или из файла.
- Создайте новый битмап с тем же размером и размером в пикселях, что и первый битмап.
- Используйте метод Bitmap.LockBits() для блокировки всего изображения для обоих объектов Bitmap в собственном формате пикселей.
- Используйте либо функцию Marshal.Copy, либо другую функцию копирования памяти, чтобы скопировать биты изображения из первого растрового изображения во второй битмап.
- Используйте метод Bitmap.UnlockBits(), чтобы разблокировать оба объекта Bitmap.
- Используйте Bitmap.Dispose(), чтобы избавиться от первого растрового изображения.
Ответ 2
Известная ошибка GDI +.
Вы не можете закрыть MemoryStream
сразу.
Скопируйте выходной массив в другой массив байтов, затем закройте поток.
Ответ 3
Попробуйте изменить image.RawFormat
на что-то вроде JPEG или PNG. Возможно, что некоторые изображения можно открывать с помощью растрового изображения, но не сохранять (по крайней мере, в исходном формате).
Ответ 4
Согласно этой теме, я использую следующий код, и он работает правильно:
using (var ms = new MemoryStream(bytes))
{
using (var bitmap = new Bitmap(ms))
{
var bitmapCopy = new Bitmap(bitmap, bitmap.Width, bitmap.Height);
return bitmapCopy;
}
}