Невозможно отобразить изображение в HttpContext.Response.OutputStream

В основном я пытаюсь визуализировать простое изображение в обработчике ASP.NET:

public void ProcessRequest (HttpContext context)
{
    Bitmap image = new Bitmap(16, 16);
    Graphics graph = Graphics.FromImage(image);

    graph.FillEllipse(Brushes.Green, 0, 0, 16, 16);

    context.Response.ContentType = "image/png";
    image.Save(context.Response.OutputStream, ImageFormat.Png);
}

Но я получаю следующее исключение:

System.Runtime.InteropServices.ExternalException: A generic error
occurred in GDI+.
    at System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder,
    EncoderParameters encoderParams)

Решение состоит в том, чтобы использовать это вместо записи изображения в OutputStream:

MemoryStream temp = new MemoryStream();
image.Save(temp, ImageFormat.Png);
byte[] buffer = temp.GetBuffer();
context.Response.OutputStream.Write(buffer, 0, buffer.Length);

Мне просто интересно, почему первый вариант проблематичен?

Изменить: HRESULT - 80004005, который является просто «общим».

Ответов (4)

Решение

Писателю действительно нужно стремиться к правильной записи в потоке.

Но в вашем последнем исходном коде убедитесь, что вы используете либо MemoryStream.ToArray () для получения правильных данных, либо, если вы не хотите копировать данные, используйте MemoryStream.GetBuffer () с MemoryStream.Length, а не длину возвращенного массива.

GetBuffer вернет внутренний буфер, используемый MemoryStream, и его длина обычно превышает длину данных, которые были записаны в поток.

Это предотвратит отправку мусора в конце потока и не испортит какой-то строгий декодер изображений, который не допускает замыкающего мусора. (И передавать меньше данных ...)

Image.Save (поток MemoryStream) действительно требует объекта MemoryStream, который можно искать. Context.Response.OutputStream предназначен только для пересылки и не поддерживает поиск, поэтому вам нужен промежуточный поток. Однако вам не нужен буфер массива байтов. Вы можете писать прямо из потока временной памяти в context.Response.OutputStream:

/// <summary>
/// Sends a given image to the client browser as a PNG encoded image.
/// </summary>
/// <param name="image">The image object to send.</param>
private void SendImage(Image image)
{
    // Get the PNG image codec
    ImageCodecInfo codec = GetCodec("image/png");

    // Configure to encode at high quality
    using (EncoderParameters ep = new EncoderParameters())
    {
        ep.Param[0] = new EncoderParameter(Encoder.Quality, 100L);

        // Encode the image
        using (MemoryStream ms = new MemoryStream())
        {
            image.Save(ms, codec, ep);

            // Send the encoded image to the browser
            HttpContext.Current.Response.Clear();
            HttpContext.Current.Response.ContentType = "image/png";
            ms.WriteTo(HttpContext.Current.Response.OutputStream);
        }
    }
}

Полнофункциональный образец кода доступен здесь:

Автоматическое создание текстовых изображений со сглаживанием с помощью ASP.NET

Я считаю, что проблема в том, что Response.OutputStream не поддерживает поиск. Чтобы сохранить PNG (или JPEG), объект изображения должен иметь возможность записывать выходные данные непоследовательно. Если я правильно помню, это сработало бы, если бы вы сохранили изображение как BMP, поскольку этот формат изображения можно записать без поиска потока.

Хорошо, я использовал оболочку для Stream (реализует Stream и передает вызовы базовому потоку), чтобы определить, что Image.Save () вызывает свойства Position и Length без проверки CanSeek, который возвращает false. Он также пытается установить для Position значение 0.

Таким образом, кажется, требуется промежуточный буфер.