Introduction
I seem to be doing a lot of applications where I have to deal with images a lot, and specifically the issue of dynamically resizing images - so I decided to write a library to make the process as simple as possible.
Background
My goals for this library were to make it easy to use, and flexible enough to be used no matter how you store your images (FileSystem, Database, Amazon Web Services, etc.). As for the discussion on which way is the "best" way to store images, I'll leave that to someone else. However, anyway you go, there's still some common things you have to think about: resizing images and maintaining aspect ratio, uniqueness of file names, etc.
Using the code
I usually try not to add extra objects when creating simple libraries like this one - but in this case, I thought it might actually help. To resize images, sometimes a lot of information is needed: the image stream, content type, desired width or height, etc. So, rather than create methods with a ton of parameters, this library will use the ImageInfo
object which holds most of this information for us. I've also split some methods up into two "manager" classes: WebManager
and FileManager
. If you're using this library on a code-behind page, then the "WebManager
" object is probably the best way to go - otherwise you'd use the "FileManager
" class.
Best of all, you can override or inherit from just about anything, so you can modify it to fit your specific situation.
The attached demo project includes all the samples described here, as well as a separate project containing the library.
Sample: Save an uploaded image and resized versions
In this first example, we'll assume that we've just posted an ASPX page, and used a FileUpload
control to select an image to upload. This code is placed in the ButtonSubmit_Click
method:
IImageInfo img = WebManager.GetImageInfo(fileUploader);
img.Path = "uploadedImages";
img.Save();
IImageInfo imgLarge = img.ResizeMe(null, 300);
imgLarge.Save("uploadedImages/large");
IImageInfo imgMedium = img.ResizeMe(185, null);
imgMedium.Save("uploadedImages/medium");
IImageInfo imgSmall = img.ResizeMe(null, 100);
imgSmall.Save("uploadedImages/small");
IImageInfo imgTiny = img.ResizeMe(50, null);
imgTiny.Save("uploadedImages/tiny");
Let's go over the example above, line by line. The first line calls the static GetImageInfo
method on the WebManager
class. This method takes the FileUpload
, populates an object that uses the IImageInfo
interface with the information of the uploaded image, and returns it.
IImageInfo img = WebManager.GetImageInfo(fileUploader);
The next line sets the Path
to the directory "uploadedImages". This is the directory that we want to save the original uploaded file into. **Please note that you must adjust the permissions on this directory to allow the .NET process to write to it. In my case, I have given "modify" privileges to three users: IIS_WPG, ASPNET, and IIS_IUSRS.
img.Path = "uploadedImages";
The next line simply calls the Save
method that is available on the ImageInfo
object. Because we didn't pass in any parameters, this method will save the uploaded image using its existing file name.
img.Save();
The next line uses the ResizeMe
method available on the ImageInfo
class to resize the image it holds, and then returns a new ImageInfo
object with the resized image. The ImageInfo
object contains all of the same information as the original image (path, filename, etc), but the PhotoStream
, Width
, and Height
are set to the resized image dimensions. The ResizeMe
method takes two nullable integers: maxHeight
and maxWidth
. In this case, we only care about the width being 300 pixels - so we can set maxHeight
to null
(or 0). Setting both the maxHeight
and maxWidth
parameters would resize the image so that neither the maxHeight
nor maxWidth
was exceeded. It would not resize the image to the exact maxHeight
and maxWidth
specified - it would still maintain the correct aspect ratio. In the example below, we pass in null
for the maxHeight
, and 300 for the width, resulting in an image resized with a width of 300 pixels.
IImageInfo imgLarge = img.ResizeMe(null, 300);
The next line calls the Save
method again, but this time, we pass in the path that we want to save the image to. In this case, we want the resized image to reside in the "uploadedImages/large" directory.
imgLarge.Save("uploadedImages/large");
(**Note: We could also prepend "/large" to the existing Path
property of the imgLarge
variable as shown below, and then call Save()
):
imgLarge.Path += "/large";
The next section does the same resize action shown above, but this time resizes the image by setting a maxHeight
, and no maxWidth
.
IImageInfo imgMedium = img.ResizeMe(185, null);
imgMedium.Save("uploadedImages/medium");
So, that's the bare bones example. However, you're not always going to save images in this way; many times, they need to be tied to specific records in a database, or be named in a specific way. Let's take a look at what we might do in some other situations.
Other examples
In the following code sample, we'll save a pointer to an image in the database first, then use the new ID in the image filename.
IImageInfo img = WebManager.GetImageInfo(fileUploader);
string[] fileNameParts = img.FileName.split('.');
string fileExtension = fileNameParts.GetValue((fileNameParts.Length - 1));
Photo p = new Photo();
p.ProductID = 1;
p.Path = "uploadedImages/photos";
p.Description = "Left side view";
p.FileExtension = fileExtension;
ProductRepository.Save(p);
int newID = p.PhotoID;
img.Path = p.Path;
img.FileName = string.Format("{0}.{1}", newID.ToString(), p.FileExtension);
img.Save();
img.ResizeMe(null, 185).Save(p.Path += "/large");
img.ResizeMe(null, 100).Save(p.Path += "/medium");
img.ResizeMe(null, 50).Save(p.Path += "/small");
The above sample shows how this library makes things a bit easier when saving product photos. In it, we save the record to the database first, then save the images to the file system using the unique ID generated when the record was created. Now, when we need to display this product photo, we can simply point to the "uploadedImages/photos/[PhotoID].[FileExtension]" file. If the smaller image is needed, simply point to the image under the "large", "medium", or "small" subfolder.
So, given the sample above, what happens if we need to delete a pPhoto? It'd be nice to delete the original image from the file system as well as all the thumbnail images that were generated. Luckily, we can do that a couple different ways. To delete the images that were created in the example above, you would do something like this:
Photo p = PhotoRepository.GetPhotoByID(id);
string[] pathsToDeleteFrom = new string[] { p.Path, p.Path +=
"/large", p.Path += "/medium", p.Path += "/small" };
WebManager.DeleteImageSetFromFileSystem(string.Format("{0}.{1}",
p.PhotoID.ToString(), p.FileExtension), pathsToDeleteFrom);
PhotoRepository.Delete(p);
The DeleteImageSetFromFileSystem
method takes in the filename and the string array of paths (directories) to delete that file from. So, in this example, the filename with this photo's PhotoID
will be deleted from each of the paths in the string array.
Conclusion
So, that's a quick look at the library. I hope you find it useful. I hope to update with more features in the future, and will post those here when I do.
Any feedback/suggestions are definitely welcome!