Tuesday 5 February 2008

ASP.net Image Upload and Resize (in C# .net 2.0, 3.0, 3.5, visual studio 2005, visual studio 2008)

Please excuse the ridiculously long title but I kinda want to make search engines love this post because I keep having to google for it.

What follows is what I have found to be the simplest and most elegant method of uploading, resizing and saving an image in ASP.NET. So let's get to it.

Ok, lets assume we have a FileUpload control (named fuImageFile) and a Button control (btnUpload) and we're going to upload the image when we click our button. The simplest method of uploading our file might look something like this

protected string savePath = "~/uploaded/";

protected void btnUpload_Click (object sender, EventArgs e) {
string fileName = fuImageFile.FileName;
string saveName = Server.MapPath(savePath) + fileName;
fuImage.SaveAs(saveName);
}


So now we can upload an image to our server, what about resizing that image? For this to work you will need to add a using directive for System.Drawing and System.Drawing.Imaging

using System.Drawing;
using System.Drawing.Imaging;

Next we'll look at how we go about resizing an image (in the form of a Bitmap object) and then at how to integrate that into our upload page.

public Bitmap ResizeBitmap (Bitmap src, int newWidth, int newHeight) {
Bitmap result = new Bitmap(newWidth, newHeight);
using ( Graphics g = Graphics.FromImage((System.Drawing.Image)result) ) {
g.DrawImage(src, 0, 0, newWidth, newHeight);
}
return result;
}


This method takes a Bitmap object and two numbers specifying the height and width of the required image. We then create a new empty Bitmap at the desired size (result) , we then obtain a Graphics object from it (NB: the Graphics object remains associated with the Bitmap) and draw our source image into it.

When we return the result we have an Bitmap object with the image data we drew into the Graphics object.

So now lets work it into our page

protected void btnUpload_Click (object sender, EventArgs e) {
string fileName = fuImageFile.FileName;

// Get the bitmap data from the uploaded file
Bitmap src = Bitmap.FromStream(fuImageFile.PostedFile.InputStream) as Bitmap;

// Resize the bitmap data
Bitmap result = ResizeBitmap (src, 200, 200);

string saveName = Server.MapPath(savePath) + fileName;
result.Save(saveName, ImageFormat.Jpeg);
}
What we do here is take the InputStream of the FileUpload's PostedFile property and create a Bitmap with it, once we have this bitmap object we can call it's Save method to save it to the local disk.

Proportional Resizing

The problem with this ResizeBitmap is that it will stretch and pull your input image to fit the dimensions supplied which 99% of the time is not what we will want. We usually want to scale an image keeping its proportions. Take a look at the ProportionallyResizeBitmap method below:


public Bitmap ProportionallyResizeBitmap (Bitmap src, int maxWidth, int maxHeight) {
// original dimensions
int w = src.Width;
int h = src.Height;

// Longest and shortest dimension
int longestDimension = (w>h)?w: h;
int shortestDimension = (w<h)?w: h;

// propotionality
float factor = ((float)longestDimension) / shortestDimension;

// default width is greater than height
double newWidth = maxWidth;
double newHeight = maxWidth/factor;

// if height greater than width recalculate
if ( w < h ) {
newWidth = maxHeight / factor;
newHeight = maxHeight;
}

// Create new Bitmap at new dimensions
Bitmap result = new Bitmap((int)newWidth, (int)newHeight);
using ( Graphics g = Graphics.FromImage((System.Drawing.Image)result) )
g.DrawImage(src, 0, 0, (int)newWidth, (int)newHeight);
return result;
}

This method takes a maxWidth and a maxHeight instead of a newWidth and newHeight, it then does a bit of maths to figure out which is the longest, the original height or the original width, works out the ratio of these two numbers and applies them appropriatley to the target size and resizes the image accordingly.

Just like ResizeBitmap, ProportionallyResizeBitmap returns a Bitmap object so you can simply substitute the call to ResizeBitmap in the button click method and it will all play nicely!

Gotchas

If you get a "Generic Error from GDI +" like Exception Details: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+ then you should check the following points:
  • Make sure the destination folder exists
  • Make sure the the local machine user ISS_WPG has write permissions to the destination folder
  • Make sure the save path is correctly escaped
[more to follow maybe]

See the Microsoft Knowlage Base article http://support.microsoft.com/?id=814675 for more posibilities

38 comments:

  1. what does the b represent ?g.DrawImage(b, 0, 0, (int)newWidth, (int)newHeight);

    ReplyDelete
  2. My bad, b should be src.

    You are drawing the source image into the result image.

    Updating the post now

    ReplyDelete
  3. Thanks, but do you have a working version of this? because some how i am still getting casting errors

    ReplyDelete
  4. Sure,

    what error are you getting?
    Drop me an email to greg[.]brant{at}twentysixleeds.com

    ReplyDelete
  5. I Greg,

    I Just sent u a mail now.

    ReplyDelete
  6. First of all thanks for the post. I'm having the following issue. The images with transparency are loosing its transparency, instead a black background replace it. Does a workaround exists? Am I doing something wrong?

    ReplyDelete
  7. Hi,

    This is because the code is saving the file as a JPEG which does not support transparency.

    If you change the line

    result.Save(saveName, ImageFormat.Jpeg);

    to

    result.Save(saveName, ImageFormat.Png);

    and upload a png image with transparency you will see it works.

    You might want to do a switch /case or a bunch of if/else if 's to save out to the correct format, or error if an image is uploaded in the wrong format, like a tiff, or a psd.

    greg

    ReplyDelete
  8. Great article, it was exactly what I was looking for, but I think your math is a littl off in the ProportionallyResizeBitmap method... I tink i should look a little more like this.

    double newWidth = maxWidth;
    double newHeight = maxWidth/factor;
    // if height greater than width recalculate
    if ( w < h )
    {
    newWidth = maxHeight / factor;
    newHeight = maxHeight;
    }

    ReplyDelete
  9. You are of course correct.

    I've updated the articel, thanks for your comment!

    ReplyDelete
  10. Works like a Charm!

    Thanks a million!

    ReplyDelete
  11. Thank You Very Much...

    This Code is much Helpfull for ASP.Net Developers..

    ReplyDelete
  12. Exactly what I was looking for. Thank you!

    ReplyDelete
  13. Exactly what I was looking for. Thank you!

    ReplyDelete
  14. Good one. Just one more question. How will you be able to use this type of dynamic resizing, if you only have one variable i.e only the max width ? This is the problem I am facing.
    I have a bunch of images that gets rezied automatically, and every now and the one just freeks out. The issue is i only get a max witdh..

    ReplyDelete
  15. the code figures out which is the largest dimension, the width or height and then scales to other one accordingly.

    If it's not quite what you're after, feel free to hack it around to suit your needs.

    ReplyDelete
  16. Brilliant article.

    About time someone wrote something understandable on this subject.

    Well done

    ReplyDelete
  17. How would I amend this code so that I could take a snippet of the image
    Eg. I have a large image which I upload but I also want to make a thumbnail. The thumbnail needs to be 50px*50px.

    ReplyDelete
  18. you are best

    ReplyDelete
  19. I have problem of a different nature. When I include
    | using System.Drawing.Imaging; |, I still bitmap class is not available. I use framework 2.0 and studio in 2008. Thanks

    ReplyDelete
  20. Have you added a reference to System.Drawing.dll.
    Check out http://www.programmersheaven.com/2/FAQ-VISUALSTUDIO-Add-Assembly-Reference

    ReplyDelete
  21. thanks, I was egging on the topic for 2hrs. And I like the title of your blog. Well said kind of feeling I get.

    It worked for me.

    I did not understand this part though:

    (w > h) ? w : h

    What actually is getting done here?

    w>h, this I understood. But (?w:h)

    What is this?

    It would be cool, if I could put some light in to this.

    from,

    digish

    ReplyDelete
  22. Hi digish, check out this article on the ternary operator. That will answer your questions on (w > h) ? w : h

    ReplyDelete
  23. Hi,

    I gave a square image, dimension: 175*175, the result is: 125*125. I dont know why this happened. I probably think, because the condition does not check, if(w=h)?w:h.

    You have to update your code, or something in my code is gone wrong. Will update, if I find mistake.

    digish

    ReplyDelete
  24. Hey digish,
    Have you tried debugging the code and stepping through it. This way you could figure out what the issue is.

    In the event that w == h. It doesn't matter which dimension you use.

    Think about it like this.

    For the shortestDimension, if width < height we use width, else we use height. So, if height is smaller, we use height.

    I don't check if w == h because, in the event they are equal, it won't matter which one you use, as they both have the same value.

    If you can step through your code and give me more of an idea about where the issue is I'll try and help you.

    Cheers

    ReplyDelete
  25. Thanks for the post. This is a great help for me. It helped me a lot where I was stuck.

    Thanks again. :)

    ReplyDelete
  26. Hi Greg,

    I've been trying out the Proportional Resize, but it doesn't always seem to work..

    In my test I have an image 1322 x 1128 and I open the file in C# then apply the method:

    Image img = Image.FromFile("testfile.jpg");
    Bitmap poster = new Bitmap(img);
    poster = ProportionallyResizeBitmap(poster, 375, 296);

    However, checking the out file before and after shows the dimensions as:

    Before: 1932 x 1624
    After: 375 x 315

    ReplyDelete
  27. Please disregard 1322x1128, my image was 1932x1624 to begin with.

    ReplyDelete
  28. Hi, its Anonymous again..

    Looking at the code I don't see how the specified Max Height would apply if the width was longer than the height.

    w = 1932
    h = 1624

    int longestDimension = 1932
    int shortestDimension = 1624

    // the factor equal to ~1.189
    float factor = 1932 / 1624

    double newWidth = 375
    double newHeight = 375 / 1.189

    // making the newHeight equal to ~315 which is larger than the specified 296

    ReplyDelete
  29. Brilliant article, nicely explained and not overloaded with code.

    A good addition to this would be how to create thumbnails, hence cropping part of the image out.

    ReplyDelete
  30. Really nice code. My customers think it is too slow though. A 145k image takes 45 seconds and they are populating their catalogs with hundreds of images. Any thoughts?

    ReplyDelete
  31. Hey Stephen,
    That doesn't sound right at all. What hardware are you using? What is the image?

    I haven't got time right now but I will do some benchmarks on the code. There's no way it should take that long!

    ReplyDelete
  32. if any1 to re size image using asp.net-2.0
    than please visit the link below http://www.raiseitsolutions.com/forum/viewtopic.php?f=4&t=3
    re size an image with a desire size using asp.net (vb) - with fine image quality.

    ReplyDelete
  33. FileUltimate is an ASP.NET file upload control which you can add directly to your existing ASP.NET (.aspx) pages.The control renders a user interface similar to "Windows Explorer" within the page which displays the contents of the target folder and accepts multiple file uploads from users. Actions can be limited by permissions and quota limits on folders. During file uploading, detailed information such as transfer speed and estimated time of completion are displayed along with the progress bar. This ASP.NET upload control supports browser upload,
    ajax upload and flash upload modes.
    net file upload

    ReplyDelete
  34. Very Nice and helpful.

    ReplyDelete
  35. ya
    i have tried it working very fine

    ReplyDelete
  36. Secοnd and impoгtantly, if уou Recreate Online Gаmеs, you would gamerѕ, the resale diligеnсe for thesе Onlinе Games is immenѕe.



    Here is my weblog :: game

    ReplyDelete