Image Transformations for Web

While IImageTransformationService provides a way to transform images (stream to stream), making transformed images available for web is a responsibility of OpenWaves.Web.ImageTransformations.IWebImageTransformationService.

public interface IWebImageTransformationService
{
    Url GetTransformedImageUrl(Url imageUrl, IImageTransformation transformation);        
}

The service allows you to obtain a url to a transformed version of the image pointed to by imageUrl parameter.
The image is transformed using the transformation implementing IImageTransformation (see Image Transformations for details on available transformations).

Usage example

The following code show how to use the service.

using OpenWaves;
using OpenWaves.ImageTransformations;
using OpenWaves.Web.ImageTransformations;

...

var service = ServiceLocator.Resolve<IWebImageTransformationService>();

var scaledImageUrl = service.GetTransformedImageUrl(Url.Parse("~/Images/foo.jpg"), new ScaleToFillTransformation(100, 100));

WebImageTransformationService configuration

Default implementation of IWebImageTransformationService can be configured trough constructor injected dependencies.

public class WebImageTransformationService : IWebImageTransformationService
{
    ...

    public WebImageTransformationService(
        IVirtualFileProvider imageFileProvider, 
        IConcurrentFileStore scaledImageStore, 
        IImageTransformationService imageTransformationService) { ... }
    ...
}

Example:

   resolver.Register<IWebImageTransformationService>(new WebImageTransformationService(
        new VirtualPathFileProvider(HostingEnvironment.VirtualPathProvider),
        new ConcurrentFileStore(new MapPathBasedFileStore("~/TransformedImages")),
        new GdiImageScalingService()));

IVirtualFileProvider

IVirtualFileProvider is where the service will look for source files to transform. In the example above the provider will be queried for "~/Images/foo.jpg".

public interface IVirtualFileProvider
{
    IVirtualFile GetFile(Url fileUrl);        
}

public interface IVirtualFile
{
    Url Url { get; }
    string Hash { get; }
    Stream Open();
}

As you can see above, implementers of the interface are responsible not only for providing a way to read from file (IVirtualFile.Open()) but also for providing a hash of a file, that can be used to detect changes to source files. Calculating a hash of a file should not be an expensive operation. Obvious implementation is to use last modified time of a file. If IVirtualFile.Hash == null, the service will not try to check for file modifications and will never invalidate once cached transformed image.

The most obvious implementation of the interface is VirtualPathFileProvider which in essence is a wrapper around VirtualPathProvider. It forwards IVirtualFile.Hash to VPP.GetFileHash() method.

Other implementations include:

EPiVirtualPathFileProvider is a wrapper for a virtual path provider that uses EPiServer.Web.Hosting.VirtualFileEx.Changed to generate IVirtualFile.Hash
CompositeVirtualFileProvider is a provider that allows for chaining of file providers

IConcurrentFileStore

IConcurrentFileStore is where the service will store transformed images.

    public interface IConcurrentFileStore
    {
        bool TryGetFileUrl(string fileName, TimeSpan timeout, out Url fileUrl);
        bool TryCreateFile(string fileName, out Url fileUrl, out Stream fileStream);

        void Delete(string fileName);
    }

Urls returned by the store must be resolvable by one of registered virtual path providers.
Implementers of the interface are responsible for synchronizing concurrent attempts to create the same file. The usage pattern of the store is the following:

IConcurrentFileStore store = ... ;

Url url;
if (store.TryGetFileUrl(fileName, timeout, out url))
{
    // the store already contains the file
    return url;
}

Stream stream;
if (store.TryCreateFile(fileName, out url, out stream))
{
    // the store did not contain the file
    // new file has been created and file stream has been returned together with the url
    using (stream)
    {
         /// write content of the file to the stream 
    }
    
    return url;
}
else
{
    // we've failed to create new file - apparently another thread must have created the file
    if (store.TryGetFileUrl(fileName, timeout, out url) == false)
    {
        throw new InvalidOperationException("The file should already be there, as we were not able to create it ourselfs.");
    }
    
    return url;
}

IImageTransformationService

IImageTRansformationService is responsible for actual scaling of the images. See Image Transformations for more details.

Last edited Sep 9, 2011 at 1:58 PM by mgrzyb, version 4

Comments

No comments yet.