Using WebClient to get Remote Images Produces Grainy GIFs and Can't Handle PNG+BMP
A few points about your GetImage method:
- When you use Image.FromStream you shouldn't close (or dispose) the stream
- If you're calling Dispose on a stream (with the using statement) you don't need to call Close
- You're writing to the stream, but then not "rewinding it" so l_image doesn't actually get any data as far as I can see (unless Image.FromStream resets the position itself). (It could be that the gif/jpg decoders rewind the stream but bmp/png don't, hence the error.)
- Why don't you just use the MemoryStream constructor which takes a byte array?
In short, I believe your GetImage method can be replaced with:
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);
}
Now, more importantly - why are you loading the image at all? Why don't you just serve the file itself as a response, setting the content type as you're already doing - or possibly just based on the extension? In other words, all of your code would become:
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();
}
A bit more error handling (including "is this a reasonable extension?") would be nice, but other than that I think it's okay. The only benefit of actually loading the image yourself is that you get to validate that it really is an image rather than a virus or something like that.
EDIT: Just out of interest, do you have a good reason why you'd want image requests to go through your server? Why would the web page author write:
<img src="http://www.mydomain.com/ImageLoader.aspx?i=http://images.mydomain.com/img/a.jpg" />
instead of
<img src="http://images.mydomain.com/img/a.jpg" />
There are some reasons why it might be useful, but in many cases it's just a waste.
Comments
-
Bullines almost 2 years
Greetings!
I'm creating a web form prototype (ImageLaoder.aspx) that will return an image so that it may be used like this simple example other Web Forms/web pages:
<img src="http://www.mydomain.com/ImageLoader.aspx?i=http://images.mydomain.com/img/a.jpg" />
So far, it loads JPGs with no problems, however GIFs look "grainy" compared to the orignals and BMPs and PNGs result in the following exception:
System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+
My code thus far looks like this:
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); }
Any ideas why GIFs are grainy and PNGs and BMPs cause exceptions?