5
@functions{

    public void GetThumbnailView(string originalImagePath, int height, int width)
    {
        //Consider Image is stored at path like "ProductImage\\Product1.jpg"

        //Now we have created one another folder ProductThumbnail to store thumbnail image of product.
        //So let name of image be same, just change the FolderName while storing image.
        string thumbnailImagePath = originalImagePath;
        originalImagePath = originalImagePath.Replace("thumb_", "");
        //If thumbnail Image is not available, generate it.
        if (!System.IO.File.Exists(Server.MapPath(thumbnailImagePath)))
        {
            System.Drawing.Image imThumbnailImage; 
            System.Drawing.Image OriginalImage = System.Drawing.Image.FromFile(Server.MapPath(originalImagePath));

            double originalWidth = OriginalImage.Width;
            double originalHeight = OriginalImage.Height;

            double ratioX = (double)width / (double)originalWidth;
            double ratioY = (double)height / (double)originalHeight;

            double ratio = ratioX < ratioY ? ratioX : ratioY; // use whichever multiplier is smaller

            // now we can get the new height and width
            int newHeight = Convert.ToInt32(originalHeight * ratio);
            int newWidth = Convert.ToInt32(originalWidth * ratio);

            imThumbnailImage = OriginalImage.GetThumbnailImage(newWidth, newHeight,
                         new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback), IntPtr.Zero);
            imThumbnailImage.Save(Server.MapPath(thumbnailImagePath), System.Drawing.Imaging.ImageFormat.Jpeg);

            imThumbnailImage.Dispose();
            OriginalImage.Dispose();
        }

    }

    public bool ThumbnailCallback() { return false; }

}

in another stackowerflow question i found this code and really liked it but while using it there was a problem occured while creating the thumbnail images as shown below:

Server Error in '/' Application.

Out of memory. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.OutOfMemoryException: Out of memory.

Source Error:

Line 199: {

Line 200: System.Drawing.Image imThumbnailImage;

Line 201: System.Drawing.Image OriginalImage = System.Drawing.Image.FromFile(Server.MapPath(originalImagePath.ToString()));

Line 202:

Line 203: double originalWidth = OriginalImage.Width;

Source File: c:\Inetpub\wwwroot\Lokal\Views\Stok\SatisRaporu.cshtml
Line: 201

my curiosity about this issue got me into the exception details and seen this :

    //
    // Summary:
    //     Creates an System.Drawing.Image from the specified file.
    //
    // Parameters:
    //   filename:
    //     A string that contains the name of the file from which to create the System.Drawing.Image.
    //
    // Returns:
    //     The System.Drawing.Image this method creates.
    //
    // Exceptions:
    //   System.OutOfMemoryException:
    //     The file does not have a valid image format.-or- GDI+ does not support the
    //     pixel format of the file.
    //
    //   System.IO.FileNotFoundException:
    //     The specified file does not exist.
    //
    //   System.ArgumentException:
    //     filename is a System.Uri.
    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    public static Image FromFile(string filename);

but all my pictures in that folder has ".jpg" extention so it seems wierd to me. if im not gonna be able to create thumbnails from ".jpg" what else i can do?

I actually want to learn about if anyone else tried this on ".jpg" files and got a problem with it? and If no problem occured what i might be doing wrong?

A little note: I do this in a view using razor syntax. I know a little about c# language and improving my knowledge about it everyday.

Edit :

How i call the function:

GetThumbnailView("../pics/thumb_" + (("0000000" + stocks.stockcode).Substring(("0000000" + stocks.stockcode).Length - 7, 7)) + ".jpg", 200, 200);
Berker Yüceer
  • 7,026
  • 18
  • 68
  • 102
  • 4
    That seems like a very odd description for an `OutOfMemoryException` to be thrown. I just lost a little bit of respect for whoever wrote that. They should validate the input and throw a different exception. – David Mar 27 '12 at 13:14
  • @David Yea tell me about it! Took my 10 minutes to understand whats going on. – Berker Yüceer Mar 27 '12 at 13:26
  • are you sure that the filename you are passing doesn't include the jpeg extension or another extension already? i mean the Server.MapPath(thumbnailImagePath) code. – JoJa Mar 27 '12 at 14:01
  • it contains ".jpg" at the end.. this is how i call the funct: `GetThumbnailView("../pics/thumb_" + (("0000000" + stocks.stockkode).Substring(("0000000" + stocks.stockkode).Length - 7, 7)) + ".jpg", 200, 200);` – Berker Yüceer Mar 27 '12 at 14:02
  • 1
    We’re using WPF to thumbnail images instead of GDI+ in a web application, and the APIs are pretty nice. They certainly allow you to distinguish format errors from OOM, while supporting a number of popular codecs and letting you know the width/height *before* you decode it. Interested? – Roman Starkov Mar 27 '12 at 14:19
  • @romkyns seems interesting id like to know more about. – Berker Yüceer Mar 27 '12 at 14:24

2 Answers2

3

A website I work on generates its thumbnails using the WPF APIs instead of GDI+. You need to add two references to your project to enable this: WindowsBase, PresentationFramework and PresentationCore. Here’s a basic example of how the code might be used:

try
{
    using (var input = File.Open(inputFilename, FileMode.Open, FileAccess.Read, FileShare.Read))
    using (var thumb = File.Open(thumbFilename, FileMode.Create, FileAccess.Write, FileShare.None))
    {
        Thumbnail(input, thumb, 200, 100);
    }
}
catch (MyException)
{
    File.Delete(thumbFilename);
}

This fits the thumbnail into a 200x100 rectangle, while preserving aspect ratio.

(The real website doesn’t do it quite like the above. What we actually do is attempt to generate the smallest thumbnail in the file upload POST handler. We use a memory stream to hold the resulting thumbnail. If the thumbnail could be generated correctly, we save the upload and the small thumbnail, otherwise we return an error response to the client. Other thumbnail sizes are generated on the fly and cached.)

Here’s the code - note that I may have messed up a bit while transforming this into something reusable, but the core bits should all be there. Note that it saves all thumbnails as JPEG, but allows multiple input formats, including JPEG and PNG. This might or might not be OK for you.

private static void Thumbnail(Stream source, Stream destination, int maxWidth, int maxHeight)
{
    int width = 0, height = 0;
    BitmapFrame frame = null;
    try
    {
        frame = BitmapDecoder.Create(source, BitmapCreateOptions.None, BitmapCacheOption.None).Frames[0];
        width = frame.PixelWidth;
        height = frame.PixelHeight;
    }
    catch
    {
        throw new MyException("The image file is not in any of the supported image formats.");
    }

    if (width > AbsoluteLargestUploadWidth || height > AbsoluteLargestUploadHeight)
        throw new MyException("This image is too large");

    try
    {
        int targetWidth, targetHeight;
        ResizeWithAspect(width, height, maxWidth, maxHeight, out targetWidth, out targetHeight);

        BitmapFrame targetFrame;
        if (frame.PixelWidth == targetWidth && frame.PixelHeight == targetHeight)
            targetFrame = frame;
        else
        {
            var group = new DrawingGroup();
            RenderOptions.SetBitmapScalingMode(group, BitmapScalingMode.HighQuality);
            group.Children.Add(new ImageDrawing(frame, new Rect(0, 0, targetWidth, targetHeight)));
            var targetVisual = new DrawingVisual();
            var targetContext = targetVisual.RenderOpen();
            targetContext.DrawDrawing(group);
            var target = new RenderTargetBitmap(targetWidth, targetHeight, 96, 96, PixelFormats.Default);
            targetContext.Close();
            target.Render(targetVisual);
            targetFrame = BitmapFrame.Create(target);
        }

        var enc = new JpegBitmapEncoder();
        enc.Frames.Add(targetFrame);
        enc.QualityLevel = 80;
        enc.Save(destination);
    }
    catch
    {
        throw new MyException("The image file appears to be corrupt.");
    }
}

/// <summary>Generic helper to compute width/height that fit into specified maxima while preserving aspect ratio.</summary>
public static void ResizeWithAspect(int origWidth, int origHeight, int maxWidth, int maxHeight, out int sizedWidth, out int sizedHeight)
{
    if (origWidth < maxWidth && origHeight < maxHeight)
    {
        sizedWidth = origWidth;
        sizedHeight = origHeight;
        return;
    }

    sizedWidth = maxWidth;
    sizedHeight = (int) ((double) origHeight / origWidth * sizedWidth + 0.5);
    if (sizedHeight > maxHeight)
    {
        sizedHeight = maxHeight;
        sizedWidth = (int) ((double) origWidth / origHeight * sizedHeight + 0.5);
    }
}
Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
  • i must admit you got pretty nice exceptions there.. trying now. – Berker Yüceer Mar 28 '12 at 06:23
  • i guess this also requires PresentationCore reference + can i use this inside a view? or have you ever tried and got any performance lacks? – Berker Yüceer Mar 28 '12 at 07:48
  • @BerkerYüceer I don’t see why you couldn’t use this in a view. In our experience the slowest thing by far is the file upload; what the server does with that before responding is not noticeably expensive. However, you definitely want to cache the thumbnails instead of generating on-the-fly; this _is_ CPU-expensive. Good enough if you do it the first time a thumbnail is requested, but far too slow to do every time someone requests a thumbnail. – Roman Starkov Mar 28 '12 at 10:47
1

The file extension doesn't really matter, it is the actual bytes of the image that matter. Most likely one of the jpgs is corrupt. You should catch the OutOfMemory exception on a per file basis and handle that appropriately.

Since you are trying to generate thumbnails, I suggest you have a default image to use if the thumbnail can't be generated. For example, most web browsers use a small box with a red X in it when the image is corrupt or missing.

See also: SO#6506089 SO#1108607 SO#1644108 SO#9237457

And for those curious about why OutOfMemoryException is thrown, see the answer to this question: Is there a reason Image.FromFile throws an OutOfMemoryException for an invalid image format?

Community
  • 1
  • 1
Jim Counts
  • 12,535
  • 9
  • 45
  • 63
  • thanks but i cant just leave a photo-not-found image on there.. If a picture exist i must generate a thumbnail so i cant use a default image for the existing pictures and how a jpg file could get corrupt? Is it posible to be caused by moving jpg files to another folder? (I dont really think this could be the reason for it but just asking.) – Berker Yüceer Mar 28 '12 at 06:18
  • anyways on a second check if i "must" use the GDI+ than ur answer is pretty usefull +1 for that reason. – Berker Yüceer Mar 28 '12 at 07:55
  • Perhaps a better phrase would have been "not understood by GDI+" see some of the linked questions for examples of that. As for the "photo not found" image, the exception will be thrown, you have to have some strategy for dealing with it. That was just one suggestion. – Jim Counts Mar 28 '12 at 12:28
  • I understand you perfectly and thanks as i said before its really usefull. – Berker Yüceer Mar 28 '12 at 13:21