GDI+ Image.FromFile has a problem - here’s how to fix it

In GDI+ you can call Image.FromFile to load an image from a file. BUT there are several issues with this call, the biggest being that GDI+ will keep the file open long after you are done with it. Here is an image loader that gets around this issue.

If you are running a high volume web site, and your images are on a SAN you’ll find this technique necessary to prevent an eventual exhaustion of filehandles.

[csharp] using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.IO; using System.Data;

namespace Utility { public static class ImageLoader { // This isn’t going to help much – you’ll run out of memory anyway on very large images – but if you are keeping several in memory it might … public const int MaximumImageDimension = 10000;

/// /// Method to safely load an image from a file without leaving the file open, /// also gets the size down to a manageable size in the case of HUGE images /// /// An Image – don’t forget to dispose of it later public static Image LoadImage (string filePath) { try { FileInfo fi = new FileInfo(filePath);

if (!fi.Exists) throw new FileNotFoundException(“Cannot find image”); if (fi.Length == 0) throw new FileNotFoundException(“Zero length image file “);

// Image.FromFile is known to leave files open, so we use a stream instead to read it using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { if (!fs.CanRead) throw new FileLoadException (“Cannot read file stream”);

if (fs.Length == 0) throw new FileLoadException(“File stream zero length”);

using (Image original = Image.FromStream(fs)) { // Make a copy of the file in memory, then release the one GDI+ gave us // thus ensuring that all file handles are closed properly (which GDI+ doesn’t do for us in a timely fashion) int width = original.Width; int height = original.Height; if (width == 0) throw new DataException(“Bad image dimension width=0″); if (height == 0) throw new DataException(“Bad image dimension height=0″);

// Now shrink it to Max size to control memory consumption if (width > MaximumImageDimension) { height = height * MaximumImageDimension / width; width = MaximumImageDimension; } if (height > MaximumImageDimension) { width = width * MaximumImageDimension / height; height = MaximumImageDimension; }

Bitmap copy = new Bitmap(width, height); using (Graphics graphics = Graphics.FromImage(copy)) { graphics.DrawImage(original, 0, 0, copy.Width, copy.Height); } return copy; } } } catch (Exception ex) { ex.Data.Add(“FileName”, filePath); throw; } } } [/csharp]

Fri Jul 30 2010 20:38:49 GMT-0700 (Pacific Daylight Time)

Next page: Constrained parallelism for the Task Parallel Library

Previous page: Lengthening short Urls in C#

Disqus goes here