Использование WebClient для получения удаленных изображений Создает зернистые GIF-файлы и не может обрабатывать PNG + BMP

StackOverflow https://stackoverflow.com/questions/499210

  •  20-08-2019
  •  | 
  •  

Вопрос

Приветствую!

Я создаю прототип веб-формы (ImageLaoder.aspx), который вернет изображение, чтобы его можно было использовать, как этот простой пример, на других веб-формах / веб-страницах:

<img src="http://www.mydomain.com/ImageLoader.aspx?i=http://images.mydomain.com/img/a.jpg" />

Пока что файлы JPG загружаются без проблем, однако GIF-файлы выглядят "зернистыми" по сравнению с оригиналами, а BMP и PNG приводят к следующему исключению:

Система.Среда выполнения.Службы взаимодействия.Исключение ExternalException:В GDI произошла общая ошибка +

Мой код на данный момент выглядит следующим образом:

protected void Page_Load(object sender, EventArgs e)
{
    string l_filePath = Request.QueryString["i"];

    System.Drawing.Image l_image = GetImage(l_filePath);
    if (l_image != null)
    {
        System.Drawing.Imaging.ImageFormat l_imageFormat = DetermineImageFormat(l_filePath);
        WriteImageAsReponse(l_image, l_imageFormat);
    }
}

private System.Drawing.Image GetImage(string filePath)
{
    WebClient l_WebClient = new WebClient();
    byte[] l_imageBytes = l_WebClient.DownloadData(filePath);

    System.Drawing.Image l_image = null;
    using (MemoryStream l_MemStream = new MemoryStream(l_imageBytes, 0, l_imageBytes.Length))
    {
        l_MemStream.Write(l_imageBytes, 0, l_imageBytes.Length);
        l_image = System.Drawing.Image.FromStream(l_MemStream, true);
        l_MemStream.Close();
    }

    return l_image;
}

private System.Drawing.Imaging.ImageFormat DetermineImageFormat(string filePath)
{
    if (filePath.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
        return System.Drawing.Imaging.ImageFormat.Jpeg;
    else if (filePath.EndsWith(".gif", StringComparison.OrdinalIgnoreCase))
        return System.Drawing.Imaging.ImageFormat.Gif;
    else if (filePath.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
        return System.Drawing.Imaging.ImageFormat.Png;
    else
        return System.Drawing.Imaging.ImageFormat.Bmp;
}

private void WriteImageAsReponse(System.Drawing.Image image, System.Drawing.Imaging.ImageFormat imageFormat)
{
    if (image == null)
        return;

    System.Drawing.Bitmap l_outputBitMap = new Bitmap(image);

    if (imageFormat == System.Drawing.Imaging.ImageFormat.Jpeg)
        Response.ContentType = "image/jpg";
    else if (imageFormat == System.Drawing.Imaging.ImageFormat.Gif)
        Response.ContentType = "image/gif";
    else if (imageFormat == System.Drawing.Imaging.ImageFormat.Png)
        Response.ContentType = "image/png";
    else
        Response.ContentType = "image/bmp";

    l_outputBitMap.Save(Response.OutputStream, imageFormat);
}

Есть идеи, почему GIF-файлы зернистые, а PNG и BMP вызывают исключения?

Это было полезно?

Решение

Несколько моментов о вашем методе получения изображения:

  • Когда вы используете Image.FromStream вам не следует закрывать (или удалять) поток
  • Если вы вызываете Dispose в потоке (с помощью инструкции using), вам не нужно вызывать Close
  • Вы записываете в поток, но затем не "перематываете его", поэтому l_image фактически не получает никаких данных, насколько я могу видеть (если только Image.FromStream не сбрасывает саму позицию).(Возможно, декодеры gif / jpg перематывают поток, но bmp / png этого не делают, отсюда и ошибка.)
  • Почему бы вам просто не использовать конструктор MemoryStream, который принимает массив байтов?

Короче говоря, я полагаю, что ваш метод getImage можно заменить на:

private Image GetImage(string filePath)
{
    WebClient l_WebClient = new WebClient();
    byte[] l_imageBytes = l_WebClient.DownloadData(filePath);
    MemoryStream l_stream = new MemoryStream(l_imageBytes);
    return Image.FromStream(l_stream);
}

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

protected void Page_Load(object sender, EventArgs e)
{
    string filePath = Request.QueryString["i"];
    string extension = l_filePath.Substring(l_filePath.LastIndexOf('.') + 1);
    Response.ContentType = "image/" + extension;
    byte[] data = new WebClient.DownloadData(filePath);
    Response.OutputStream.Write(data, 0, data.Length);
    Response.End();
}

Немного больше обработки ошибок (включая "является ли это разумным расширением?") было бы неплохо, но в остальном, я думаю, все в порядке.Единственное преимущество самостоятельной загрузки изображения заключается в том, что вы можете убедиться, что оно действительно является изображение, а не вирус или что-то в этом роде.

Редактировать:Просто из интереса, есть ли у вас веская причина, по которой вы бы хотеть запросы изображений должны проходить через ваш сервер?Зачем автору веб-страницы писать:

<img src="http://www.mydomain.com/ImageLoader.aspx?i=http://images.mydomain.com/img/a.jpg" />

вместо того , чтобы

<img src="http://images.mydomain.com/img/a.jpg" />

Там являются некоторые причины, по которым это может быть полезно, но во многих случаях это просто пустая трата времени.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top