Recursive AI

Create a thumbnail in C#

All oneimage recipes

Call Image.Thumbnail with the file and the largest width you want, then save. It is a resize with the defaults a published copy needs: the photo is turned upright, never enlarged, converted to sRGB and stripped of EXIF, so no GPS position leaks. The program below uses a public test photo that is stored sideways, the way phones store many portraits.

Terminal
dotnet new console -o Thumbnail && cd Thumbnail
dotnet add package RecursiveAI.OneImage --version 1.10.4
repo=https://raw.githubusercontent.com/recurser/exif-orientation-examples
curl -L -o phone.jpg $repo/master/Portrait_6.jpg

OneImage needs the .NET 10 SDK. Put this in Program.cs and run dotnet run:

C#
using OneImage;
 
ImageInfo stored = Image.Identify("phone.jpg");
Console.WriteLine($"phone.jpg: stored {stored.Width}x{stored.Height}, "
                + $"orientation {stored.Metadata.Orientation}");
 
// At most 400 px wide, upright, sRGB, EXIF/XMP/IPTC removed.
Image.Thumbnail("phone.jpg", 400).Save("thumb.jpg");
 
// A 200x200 square that fills the box, for avatars and grids, as WebP.
Image.Thumbnail("phone.jpg", 200, 200, fit: Fit.Cover).Save("square.webp");
 
foreach (string file in new[] { "thumb.jpg", "square.webp" })
{
    ImageInfo info = Image.Identify(file);
    string exif = info.Metadata.ReadExif() is null ? "no EXIF" : "EXIF kept";
    long kb = new FileInfo(file).Length / 1024;
    Console.WriteLine($"{file}: {info.Width}x{info.Height}, {exif}, {kb} KB");
}
Text
phone.jpg: stored 1800x1200, orientation 6
thumb.jpg: 400x600, no EXIF, 33 KB
square.webp: 200x200, no EXIF, 3 KB

The photo is stored as 1800×1200 with an EXIF orientation of 6 (“rotate 90° to show”), and both thumbnails come out upright. In order, Thumbnail applies the orientation, resizes (decoding a JPEG at 1/2, 1/4 or 1/8 scale when that is enough), converts to sRGB with a compact embedded profile, and removes EXIF, XMP and IPTC. The output format follows the extension.

Thumbnails for a folder

Image.TryLoad returns false for files that aren’t images or are damaged, so one bad file doesn’t stop the batch:

C#
using OneImage;
 
Directory.CreateDirectory("photos");
File.Copy("phone.jpg", "photos/phone.jpg", overwrite: true);
File.WriteAllText("photos/notes.txt", "not an image");
Directory.CreateDirectory("thumbs");
 
foreach (string path in Directory.EnumerateFiles("photos"))
{
    if (!Image.TryLoad(path, out Image? image))
    {
        Console.WriteLine($"skipped {Path.GetFileName(path)}");
        continue;
    }
    string name = Path.GetFileNameWithoutExtension(path) + ".webp";
    string target = Path.Combine("thumbs", name);
    Image.Thumbnail(image, 320).Save(target);
    Console.WriteLine($"{target}: {Image.Identify(target).Width} px wide");
}
Text
skipped notes.txt
thumbs/phone.webp: 320 px wide

Thumbnail also takes a Stream or a byte[], for uploads and blob storage.

Options

  • height: and fit: set a box: Fit.Cover fills it and crops, with anchor: Anchor.Smart to keep the busiest part; Fit.Pad fits and pads.
  • upscale: true allows enlarging a source smaller than the box.
  • keepMetadata: true keeps EXIF, XMP and IPTC.

Limits

  • It never enlarges by default. A 300 px source asked for at 400 stays 300 px wide. For an exact size, use Resize.
  • It strips metadata. Copyright and caption fields in EXIF or IPTC go too, unless you pass keepMetadata: true.
  • SVG, PDF and PSD need their add-on package registered first; see SVG to PNG and PDF to image.

To serve thumbnails from URLs like photo.jpg?width=400, see Resize images on the fly in ASP.NET Core. Every parameter is in the guide.