Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating thumbnail images with C#

@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);
like image 786
Berker Yüceer Avatar asked Oct 08 '22 07:10

Berker Yüceer


2 Answers

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);
    }
}
like image 98
Roman Starkov Avatar answered Oct 12 '22 20:10

Roman Starkov


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?

like image 41
Jim Counts Avatar answered Oct 12 '22 20:10

Jim Counts