SkillAgentSearch skills...

ImageWizard

Image processing webservice based on ASP.NET Core and ImageSharp / SkiaSharp / SvgNet / DocNET

Install / Use

/learn @usercode/ImageWizard

README

ImageWizard

A ASP.NET Core service / middleware to resize your images on the fly as alternative for thumbor.

License: MIT NuGet Docker

Demo: imagewizard.net

Features

  • loader:
    • Http (streaming mode)
    • File (use IFileProvider)
    • YouTube (get thumbnail)
    • Gravatar
    • OpenGraph
  • caches:
    • File
    • Distributed cache
    • MongoDB
  • image filters: resize, crop, rotate,..
  • common image effects like grayscale and blur are available
  • create your custom data filter
  • pdf filters: page-to-image for documents
  • url is protected by a HMACSHA256 signature to prevent DDoS attacks
  • can handle the device pixel ratio (DPR)
  • support for cache control and ETag
  • enable range processing by http request
  • use RecyclableMemoryStream for smarter memory management (IStreamPool)
  • cleanup service

Example

https://localhost/image/cGiAwFYGYWx0SzO0YyCidWIfkdlUYrVgBwbm7bcTOjE/resize(200,200)/grayscale()/jpg(90)/fetch/https://upload.wikimedia.org/wikipedia/commons/b/b7/Europe_topography_map.png

| Description | Url segment | |---------------------------------|-----------------| | base path | "image" | | signature based on HMACSHA256 | "cGiAwFYGYWx0SzO0YyCidWIfkdlUYrVgBwbm7bcTOjE" or "unsafe" (if enabled) | | any filters | "resize(200,200)/grayscale()/jpg(90)" | | loader type | "fetch" | | loader source | https://upload.wikimedia.org/wikipedia/commons/b/b7/Europe_topography_map.png |

Loader

| Name | Loader type | Loader source | NuGet | |-----------------------------------|-----------------|----|---| | Http loader | fetch | absolute or relative url | NuGet | File loader | file | relative path to file | NuGet | YouTube loader | youtube | video id | NuGet | Gravatar loader | gravatar | encoded email address | NuGet | OpenGraph loader | opengraph | absolute url | NuGet | Azure loader | azure | relative path to file | NuGet | PuppeteerSharp loader | screenshot | absolute url | NuGet

Cache

| Name | Description | NuGet | |----------------------|--------------------|-----------| | File cache | Meta and blob file path based on cache id. | NuGet | | Distributed cache | MS SQL, Redis | NuGet | | MongoDB cache | Use GridFS | NuGet |

Pipeline

| Name | Mime type | NuGet | |------------------------------------|-----------------|------------| | ImageSharp | image/jpeg, image/png, image/gif, image/bmp, image/webp, image/tga | NuGet | SkiaSharp | image/jpeg, image/png, image/gif, image/bmp, image/webp | NuGet | SvgNet | image/svg+xml | NuGet | DocNET | application/pdf | NuGet

How to use it

services.AddImageWizard();

//or

services.AddImageWizard(options => 
                       {
                           options.AllowUnsafeUrl = true;
                           options.AllowedDPR = new double[] { 1.0, 1.5, 2.0, 3.0, 4.0 };
                           options.Key = new byte[64] { .. };
                           options.UseETag = true;                                                
                           options.CacheControl.IsEnabled = true;
                           options.CacheControl.MaxAge = TimeSpan.FromDays(365);
                           options.CacheControl.MustRevalidate = false;
                           options.CacheControl.Public = true;
                           options.CacheControl.NoCache = false;
                           options.CacheControl.NoStore = false;
                           //select automatically the compatible mime type by request header
                           options.UseAcceptHeader = true;
			   options.RefreshLastAccessInterval = TimeSpan.FromMinutes(1);
			   options.FallbackHandler = (state, url, cachedData) =>
			    {
				//use the existing cached data if available?
				if (cachedData != null)
				{
				    return cachedData;
				}

				//load fallback image
				FileInfo fallbackImage = state switch
				{
				    LoaderResultState.NotFound => new FileInfo(@"notfound.jpg"),
				    LoaderResultState.Failed => new FileInfo(@"failed.jpg"),
				   _ => throw new Exception()
				};

				if (fallbackImage.Exists == false)
				{
				    return null;
				}

				//convert FileInfo to CachedData
				return fallbackImage.ToCachedData();
			    };
                       })
            //registers ImageSharp pipeline for specified mime types
           .AddImageSharp(c => c
                .WithMimeTypes(MimeTypes.WebP, MimeTypes.Jpeg, MimeTypes.Png, MimeTypes.Gif)
                .WithOptions(x =>
                            {
                                x.ImageMaxHeight = 4000;
                                x.ImageMaxWidth = 4000;
                            })
                //Adds your custom filters
                .WithFilter<BlurFilter>()
		//Executes custom action before the pipeline is started.
                .WithPreProcessing(x =>
                            {
                                x.Image.Mutate(m => m.AutoOrient());
                            })
		//Executes custom action after the pipeline is finished.
                .WithPostProcessing(x =>
                            {
			    	//blurs all images
                                x.Image.Mutate(m => m.Blur());
                              
                                //overrides target format (Jpeg to WebP)
				if (x.ImageFormat is JpegFormat)
				{
                                    x.ImageFormat = new WebPFormat() { Lossless = false };
				}
				//overrides target format (Png to WebP)
				else if (x.ImageFormat is PngFormat)
				{
                                    x.ImageFormat = new WebPFormat() { Lossless = true };
				}
				
				//overrides metadata
				x.Image.Metadata.ExifProfile = new ExifProfile();
                        	x.Image.Metadata.ExifProfile.SetValue(ExifTag.Copyright, "ImageWizard");
                            }))
           //.AddSkiaSharp()
           .AddSvgNet()
	   .AddDocNET()
           //uses file cache (relative or absolute path)
           .SetFileCache(options => options.Folder = "FileCache") 
           //or MongoDB cache
           .SetMongoDBCache(options => options.Hostname = "localhost")
           //or distributed cache
           .SetDistributedCache()
           //adds some loaders
           .AddFileLoader(options => options.Folder = "FileStorage")
           .AddHttpLoader(options => 
          	{
		   //checks every time for a new version of the original image.
		   options.RefreshMode = LoaderRefreshMode.EveryTime;

		   //sets base url for relative urls
		   options.DefaultBaseUrl = "https://mydomain";

		   //allows only relative urls 
		   //(use base url from request or DefaultBaseUrl from options)
		   options.AllowAbsoluteUrls = false;

		   //allows only specified hosts
		   options.AllowedHosts = new [] { "mydomain" };

		   //adds custom http header like apikey to prevent 
		   //that user can download the original image
		   options.SetHeader("ApiKey", "123456");
     		})
           .AddYoutubeLoader()
           .AddGravatarLoader()
	   .AddOpenGraphLoader()
           .AddAnalytics()
	    //Adds a background service which removes cached data based on defined CleanupReason.
            //The cache needs to implements ICleanupCache.
            .AddCleanupService(x =>
		    {
			//Duration between the cleanup actions. (Default: 1 day)
			x.Interval = TimeSpan.FromMinutes(1);

			//Removes cached data which are older than defined duration. 
			x.OlderThan(TimeSpan.FromMinutes(2));

			//Removes cached data which are last used since defined duration. 
			x.LastUsedSince(TimeSpan.FromMinutes(2));

			//Removes cached data which are expired (based on the loader result).
			x.Expired();
		    })
           ;
//default path ("/image")

//use middleware
app.UseImageWizard(x =>
		{
			//default path  ("/analytics")

Related Skills

View on GitHub
GitHub Stars79
CategoryDevelopment
Updated1d ago
Forks12

Languages

C#

Security Score

100/100

Audited on Mar 27, 2026

No findings