L'utilisation de WebClient pour obtenir des images distantes produit des GIF granuleux et ne peut pas gérer PNG+BMP

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

  •  20-08-2019
  •  | 
  •  

Question

Salutations!

Je crée un prototype de formulaire Web (ImageLaoder.aspx) qui renverra une image afin qu'elle puisse être utilisée comme cet exemple simple sur d'autres formulaires Web/pages Web :

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

Jusqu'à présent, il charge les JPG sans problème, cependant les GIF semblent "granuleux" par rapport aux originaux et les BMP et PNG entraînent l'exception suivante :

System.Runtime.InteropServices.ExternalException :Une erreur générique s'est produite dans GDI+

Jusqu'à présent, mon code ressemble à ceci :

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);
}

Avez-vous une idée de la raison pour laquelle les GIF sont granuleux et les PNG et BMP provoquent des exceptions ?

Était-ce utile?

La solution

Quelques points sur votre méthode GetImage :

  • Lorsque vous utilisez Image.FromStream, vous ne devez pas fermer (ou supprimer) le flux
  • Si vous appelez Dispose sur un flux (avec l'instruction using), vous n'avez pas besoin d'appeler Close
  • Vous écrivez dans le flux, mais vous ne le « rembobinez » donc l_image n'obtient aucune donnée pour autant que je sache (à moins que Image.FromStream ne réinitialise la position elle-même).(Il se peut que les décodeurs gif/jpg rembobinent le flux mais pas bmp/png, d'où l'erreur.)
  • Pourquoi n'utilisez-vous pas simplement le constructeur MemoryStream qui prend un tableau d'octets ?

En bref, je pense que votre méthode GetImage peut être remplacée par :

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);
}

Maintenant, plus important encore : pourquoi chargez-vous l’image ?Pourquoi ne fournissez-vous pas simplement le fichier lui-même comme réponse, en définissant le type de contenu comme vous le faites déjà - ou peut-être simplement en fonction de l'extension ?En d’autres termes, tout votre code deviendrait :

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();
}

Un peu plus de gestion des erreurs (y compris « est-ce une extension raisonnable ? ») serait bien, mais à part ça, je pense que ça va.Le seul avantage de charger l'image vous-même est que vous pouvez valider qu'elle est réellement est une image plutôt qu'un virus ou quelque chose comme ça.

MODIFIER:Juste par intérêt, avez-vous une bonne raison pour laquelle vous vouloir les demandes d'images doivent passer par votre serveur ?Pourquoi l'auteur de la page Web écrirait-il :

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

au lieu de

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

sont quelques raisons pour lesquelles cela pourrait être utile, mais dans de nombreux cas, ce n'est qu'un gaspillage.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top