<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="http://jamessouth.me/blog/rss/xslt"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>James Jackson-South</title>
    <link>http://jamessouth.me/blog/</link>
    <description>Microsoft MVP, Creator of ImageSharp, ImageProcessor and ResponsiveBP, Web developer, Lifter of heavy weights, and all round nice guy.</description>
    <generator>Articulate, blogging built on Umbraco</generator>
    <item>
      <guid isPermaLink="false">1135</guid>
      <link>http://jamessouth.me/archive/imagesharpweb-my-continued-plans-for-world-image-dominance/</link>
      <category>System.String[]</category>
      <title>ImageSharp.Web  (My continued plans for world image dominance)</title>
      <description>&lt;h1&gt;I better build this before someone else does!&lt;/h1&gt;
&lt;p&gt;I recently read a series of blog posts by &lt;a href="https://andrewlock.net/using-imagesharp-to-resize-images-in-asp-net-core-a-comparison-with-corecompat-system-drawing/"&gt;Andrew Lock&lt;/a&gt; comparing &lt;strong&gt;ImageSharp&lt;/strong&gt; to &lt;strong&gt;CoreCompat.System.Drawing&lt;/strong&gt; in which he creates a simple ASP.NET Core &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware"&gt;Middleware&lt;/a&gt; for resizing image via a URI API. They're really, really great and I suggest you read them. &lt;/p&gt;
&lt;p&gt;These posts got me thinking though... I knew I was always going to build a URI API, I just didn't know when I was going to get around to it. I mean... I spend every spare waking hour plus ones I should be sleeping just now building &lt;a href="https://github.com/JimBobSquarePants/ImageSharp"&gt;ImageSharp&lt;/a&gt; (Big HINT &lt;a href="https://github.com/JimBobSquarePants/ImageSharp/issues?q=is:issue%20is:open%20label:%22help%20needed%22"&gt;help me finish the bloody thing&lt;/a&gt;, it's been TWO YEARS!)&lt;/p&gt;
&lt;p&gt;It had to happen sooner rather than later as people are getting twitchy and &lt;strong&gt;ImageSharp&lt;/strong&gt; is getting very close to a beta release, so inspired by Andrew's brilliant writing I got busy...&lt;/p&gt;
&lt;h2&gt;Introducing ImageSharp.Web&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;ImageSharp.Web&lt;/strong&gt; is everything I wished &lt;a href="http://imageprocessor.org/imageprocessor-web/"&gt;ImageProcessor.Web&lt;/a&gt; was and I've only been writing the code for two days. I'm that happy with it. :)&lt;/p&gt;
&lt;p&gt;It runs on ASP.NET Core Middleware, is super extensible, and, should be able to support all your web-based imaging needs once finished. &lt;/p&gt;
&lt;p&gt;You can see the codebase as of time of writing &lt;a href="https://github.com/JimBobSquarePants/ImageSharp/tree/eff6fed0eb1676c4e305be2b029255f6eb6fbdfe/src/ImageSharp.Web"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So how does it work?&lt;/p&gt;
&lt;h2&gt;The Pipeline&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;The hip bone's connected to the...&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Middleware intercepts the current request, an &lt;code&gt;IUriParser&lt;/code&gt; parses the request looking for commands and creates a dictionary of results.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;IImageService&lt;/code&gt; is assigned to the request. This will grab the image if required. &lt;/li&gt;
&lt;li&gt;An &lt;code&gt;IImageCache&lt;/code&gt; checks the cache for any previously processed results, if one found, we return the correct response (304, 200) and exit.&lt;/li&gt;
&lt;li&gt;Non cached? We process the file using a collection of &lt;code&gt;IImageWebProcessor&lt;/code&gt; instances using our collected commands via a &lt;code&gt;CommandParser&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The resultant image is cached using the &lt;code&gt;IImageCache&lt;/code&gt; and we return the result with the correct headers.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Simples!&lt;/p&gt;
&lt;p&gt;If all goes well you get something like the following. &lt;/p&gt;
&lt;p&gt;&lt;img src="http://jamessouth.me/blog/media/1055/is-web-output.png" alt="Output example for ImageSharp.Web" /&gt;&lt;/p&gt;
&lt;p&gt;So lets examine some of the interfaces I just mentioned. &lt;/p&gt;
&lt;h2&gt;Behind the Curtain&lt;/h2&gt;
&lt;p&gt;All the interfaces described in the pipeline are injected via the &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection"&gt;ASP.NET Core Dependency Injection&lt;/a&gt; container so you can replace them at will.&lt;/p&gt;
&lt;h2&gt;IUriParser&lt;/h2&gt;
&lt;p&gt;This is a neat little interface inspired by some feedback from Andrew that allows you to choose how commands are parsed from the URI. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface IUriParser
{
    IDictionary&amp;lt;string, string&amp;gt; ParseUriCommands(HttpContext context);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By default &lt;strong&gt;ImageSharp.Web&lt;/strong&gt; will parse &lt;a href="https://github.com/JimBobSquarePants/ImageSharp/blob/eff6fed0eb1676c4e305be2b029255f6eb6fbdfe/src/ImageSharp.Web/Commands/QueryCollectionUriParser.cs"&gt;querystring parameters&lt;/a&gt; but there's nothing to stop you from replacing that with something else. Maybe you want to parse something like &lt;code&gt;/resize/{width}/{height}/{path}&lt;/code&gt; who knows?!&lt;/p&gt;
&lt;h2&gt;IImageService&lt;/h2&gt;
&lt;p&gt;This interface is very similar to the ones found in &lt;a href="http://imageprocessor.org/imageprocessor-web/extending/#iimageservice"&gt;ImageProcessor.Web&lt;/a&gt; except it's a bit simpler&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface IImageService
{
    string Key { get; set; }

    IDictionary&amp;lt;string, string&amp;gt; Settings { get; set; }

    Task&amp;lt;bool&amp;gt; IsValidRequestAsync(HttpContext context, ILogger logger, string path);

    Task&amp;lt;byte[]&amp;gt; ResolveImageAsync(HttpContext context, ILogger logger, string path);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;key&lt;/code&gt; property there indicates an identifier in the URI that we use to choose which service to implement e.g &lt;code&gt;http://mysite.com/remote/http://remote-image.jpg?width=200&lt;/code&gt; would resolve to a &lt;code&gt;RemoteImageService&lt;/code&gt; (not built yet) as it uses the &lt;code&gt;remote&lt;/code&gt; prefix. Any implementations without a prefix match all requests.&lt;/p&gt;
&lt;p&gt;I've put together one so far, the &lt;code&gt;PhysicalFileImageService&lt;/code&gt;. You can see the code for that &lt;a href="https://github.com/JimBobSquarePants/ImageSharp/blob/eff6fed0eb1676c4e305be2b029255f6eb6fbdfe/src/ImageSharp.Web/Services/PhysicalFileImageService.cs"&gt;here&lt;/a&gt;. It's pretty simple but leverages the DI container to get certain functionality.&lt;/p&gt;
&lt;h2&gt;IImageCache&lt;/h2&gt;
&lt;p&gt;I didn't want to build this at first as I wanted to use the built in primitives like &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/api/microsoft.extensions.caching.distributed.idistributedcache"&gt;&lt;code&gt;IDistributedCache&lt;/code&gt;&lt;/a&gt; but the API of those primitives was both more complicated than I needed and also lacked the functionality to check if a cached item existed without retrieving it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface IImageCache
{
    IDictionary&amp;lt;string, string&amp;gt; Settings { get; set; }

    Task&amp;lt;CachedBuffer&amp;gt; GetAsync(string key);

    Task&amp;lt;CachedInfo&amp;gt; IsExpiredAsync(string key, DateTime minDateUtc);

    Task&amp;lt;DateTimeOffset&amp;gt; SetAsync(string key, byte[] value, int length);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's again pretty simple but contains enough information to create any kind of cache you require. I've written a &lt;code&gt;PhysicalFileSystemCache&lt;/code&gt; &lt;a href="https://github.com/JimBobSquarePants/ImageSharp/blob/eff6fed0eb1676c4e305be2b029255f6eb6fbdfe/src/ImageSharp.Web/Caching/PhysicalFileSystemCache.cs"&gt;implementation&lt;/a&gt; so far for non-multi-tenant applications but I plan on creating others for Azure etc. (&lt;strong&gt;I will probably charge a small fee for those though as I need to start making some money off the back of my work&lt;/strong&gt; )  
&lt;/p&gt;
&lt;h2&gt;IImageWebProcessor&lt;/h2&gt;
&lt;p&gt;This is how we actually process the image. We pass our commands and process the image accordingly. There's an overload to &lt;code&gt;Image&amp;lt;TPixel&amp;gt;&lt;/code&gt; that loops through available processors. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface IImageWebProcessor
{
    IEnumerable&amp;lt;string&amp;gt; Commands { get; }

    Image&amp;lt;TPixel&amp;gt; Process&amp;lt;TPixel&amp;gt;(Image&amp;lt;TPixel&amp;gt; image, ILogger logger, IDictionary&amp;lt;string, string&amp;gt; commands)
        where TPixel : struct, IPixel&amp;lt;TPixel&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here's the &lt;a href="https://github.com/JimBobSquarePants/ImageSharp/blob/eff6fed0eb1676c4e305be2b029255f6eb6fbdfe/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs"&gt;source code&lt;/a&gt; for the ResizeWebProcessor. This allows you to choose all the available resize options including the size, mode, and sampler. I'll knock up another couple of processors also that ship out-of-the-box too. AutoRotate, Format &amp;amp; BackgroundColor. Any additional ones will require $$ I'm afraid.&lt;/p&gt;
&lt;h2&gt;Commands&lt;/h2&gt;
&lt;p&gt;Parsing input commands is a non-trivial task. The web is a hostile environment and you not only have to deal with potential attacks, you have to deal with developers who are not keen on reading documentation. As such I have had to write a pluggable system of &lt;code&gt;ICommandConverter&lt;/code&gt; &lt;a href="https://github.com/JimBobSquarePants/ImageSharp/tree/eff6fed0eb1676c4e305be2b029255f6eb6fbdfe/src/ImageSharp.Web/Commands"&gt;implementations&lt;/a&gt; that are used by the &lt;code&gt;CommandParser&lt;/code&gt; singleton to deal with common parsing tasks. Again, this is extensible.&lt;/p&gt;
&lt;h2&gt;Rich Returns&lt;/h2&gt;
&lt;p&gt;The Middleware returns the correct response type for processed images (200, 304) and instructs the browser to cache the images for a configurable number of days (we max out by default at 365 days but of course this is configurable)!&lt;/p&gt;
&lt;p&gt;As such you will see a server response like the following.&lt;/p&gt;
&lt;h4&gt;Default Response&lt;/h4&gt;
&lt;p&gt;The result is processed and returned as normal.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jamessouth.me/blog/media/1056/is-network.jpg" alt="Raw image processing response shown in the network tab" /&gt;&lt;/p&gt;
&lt;h4&gt;Cached Response&lt;/h4&gt;
&lt;p&gt;The result is cached so no request is made to the server.. Nippy!&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jamessouth.me/blog/media/1057/is-network-cached.jpg" alt="Cached image processing response shown in the network tab" /&gt;&lt;/p&gt;
&lt;p&gt;There'll be an event that you can tap into also to add additional headers like Cors headers.&lt;/p&gt;
&lt;h2&gt;Gimme, Gimme, Gimme!&lt;/h2&gt;
&lt;p&gt;So when can you get your hands on this? &lt;/p&gt;
&lt;p&gt;Well... As soon as the main &lt;strong&gt;ImageSharp&lt;/strong&gt; library is ready for primetime I'll release a Nuget package for &lt;strong&gt;ImageSharp.Web&lt;/strong&gt;. By then any issues will be long ironed out and you'll have a great solution to you image processing needs in .NET Core. &lt;/p&gt;
&lt;p&gt;Of course, the sooner you give us a hand, the sooner that can happen ;)&lt;/p&gt;
&lt;p&gt;Please comment below, your feedback is super useful!&lt;/p&gt;
</description>
      <pubDate>Fri, 19 May 2017 06:08:11 Z</pubDate>
      <a10:updated>2017-05-19T06:08:11Z</a10:updated>
    </item>
  </channel>
</rss>