views:

2178

answers:

3

Ok, I'm sorry, this is probably a noob question but I'm kinda stuck.

So what I'm doing (on my asp.net application) is loading an image from the file system:

System.Drawing.Image tempImage;
tempImage = System.Drawing.Image.FromFile(HttpContext.Server.MapPath(originalPath));

Then I do some resizing:

tempImage = my awesomeResizingFunction(tempImage, newSize);

and intend to save it to the file system in another location using this:

string newPath = "/myAwesomePath/newImageName.jpg";
tempImage.Save(newPath);

and what I get is this error:

"A generic error occurred in GDI+."

I know the image is "ok" because I can write it out to the browser and see the resized image, I only get the error when I try to save it. I'm kinda new and stuck, am I doing this totally wrong? (Well, i guess that's obvious but you know what I mean...)

A: 

Is it possible the original stream backing the original image has been closed? If the stream behind a Bitmap has been closed, you start getting GDI+ errors. I ran into this a lot when we added image processing to our website.

If you open up the Bitmap object in the Visual Studio debugger, do you see exceptions instead of the values of the properties? If so, it's not a problem with the save operation, but the GDI+ layer has lost the ability to process the object, period.

What I found was I needed to keep track of the MemoryStreams belonging to my Bitmaps and keep them all together. Resizing an image resulted in a new MemoryStream with a new Bitmap image.

I ended up creating this simple class (trimmed some extra properties unneeded here):

public class UploadedImage : IDisposable
{
    private Bitmap _img = null;
    private Stream _baseStream = null;


    /// <summary>
    /// The image object.  This must always belong to BaseStream, or weird things can happen.
    /// </summary>
    public Bitmap Img
    {
        [DebuggerStepThrough]
        get { return _img; }
        [DebuggerStepThrough]
        set { _img = value; }
    }

    /// <summary>
    /// The stream that stores the image.  This must ALWAYS belong to Img, or weird things can happen.
    /// </summary>
    public Stream BaseStream
    {
        [DebuggerStepThrough]
        get { return _baseStream; }
        [DebuggerStepThrough]
        set { _baseStream = value; }
    }

    [DebuggerStepThrough]
    public void Dispose()
    {
        if (Img != null)
            Img.Dispose();

        if (BaseStream != null)
            BaseStream.Close();

        _attached = false;
    }
}

Now, I was dealing with images uploaded to our website, and what I found was that when Asp.Net recycled the stream attached to the Request, all the sudden image operations started flipping out. So, my solution, whether this was the best way to do it or not, was to copy the data from the upload stream to my own MemoryStream, load the image from that, and stick both into this container. And wherever I created a new image from an old one, I always kept the stream and the image together.

I hope this helps.

EDIT: I'm also interested in seeing how you're doing the image resizing. This is a snippet of how I did ours:

temp = new Bitmap(newWidth, newHeight, PIXEL_FORMAT);
temp.SetResolution(newHorizontalRes, newVerticalRes);
gr = Graphics.FromImage(temp);

//
// This copies the active frame from 'img' to the new 'temp' bitmap.
// Also resizes it and makes it super shiny.  Sparkle on, mr image dude.
// 
Rectangle rect = new Rectangle(0, 0, newWidth, newHeight);
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.SmoothingMode = SmoothingMode.HighSpeed;
gr.PageUnit = GraphicsUnit.Pixel;
gr.DrawImage(img, rect);

//
// Image copied onto the new bitmap.  Save the bitmap to a fresh memory stream.
//
retval = new UploadedImage();
retval.BaseStream = (Stream)(new MemoryStream());

temp.Save(retval.BaseStream, ImageFormat.Jpeg);
retval.Img = temp;
yodaj007
Note importantly (http://msdn.microsoft.com/en-us/library/system.drawing.aspx) > Classes within the System.Drawing namespace are not supported for use within a Windows or ASP.NET service.
Marc Gravell
Not sure what you're trying to say. We're creating and manipulating images on the fly on our website.
yodaj007
A: 

Are you sure that the originalPath and newPath point to different files ? When you use Image.FromFile, the file remains locked until you call Dispose on the Image, which can lead to the exception you mentioned. You could load the image like that instead :

Image tempImage = null;
using (FileStream fs = new FileStream(originalPath, FileMode.Open, FileAccess.Read))
{
    tempImage = Image.FromStream(fs);
}
...

This approach guarantees that the file is closed at the end of the using block

Thomas Levesque
+1  A: 

plz try this code...I have used the same code for resizing image and saving. if you get any problem then plz let me know.

System.Drawing.Bitmap bmpOut = new System.Drawing.Bitmap(NewWidth, NewHeight);
    System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmpOut);
    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
    g.FillRectangle(System.Drawing.Brushes.White, 0, 0, NewWidth, NewHeight);
    g.DrawImage(new System.Drawing.Bitmap(fupProduct.PostedFile.InputStream), 0, 0, NewWidth, NewHeight);
    MemoryStream stream = new MemoryStream();
    switch (fupProduct.FileName.Substring(fupProduct.FileName.IndexOf('.') + 1).ToLower())
    {
        case "jpg":
            bmpOut.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
            break;
        case "jpeg":
            bmpOut.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
            break;
        case "tiff":
            bmpOut.Save(stream, System.Drawing.Imaging.ImageFormat.Tiff);
            break;
        case "png":
            bmpOut.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
            break;
        case "gif":
            bmpOut.Save(stream, System.Drawing.Imaging.ImageFormat.Gif);
            break;
    }
    String saveImagePath = Server.MapPath("../") + "Images/Thumbnail/" + fupProduct.FileName.Substring(fupProduct.FileName.IndexOf('.'));
    bmpOut.Save(saveImagePath);

where fupProduct is fileupload control ID

Muhammad Akhtar
You could improve this by using Path.GetExtension instead of IndexOf('.'), though you'd have to modify the switch slightly http://msdn.microsoft.com/en-us/library/system.io.path.getextension.aspx
RichardOD
Thanks RichardOD.....
Muhammad Akhtar
Note importantly (http://msdn.microsoft.com/en-us/library/system.drawing.aspx) > Classes within the System.Drawing namespace are not supported for use within a Windows or ASP.NET service.
Marc Gravell