diff --git a/src/ImageProcessor.Web/HttpModules/ImageProcessingModule.cs b/src/ImageProcessor.Web/HttpModules/ImageProcessingModule.cs
index da41a95734..a1b968cd86 100644
--- a/src/ImageProcessor.Web/HttpModules/ImageProcessingModule.cs
+++ b/src/ImageProcessor.Web/HttpModules/ImageProcessingModule.cs
@@ -19,6 +19,9 @@ namespace ImageProcessor.Web.HttpModules
using ImageProcessor.Web.Caching;
using ImageProcessor.Web.Config;
using ImageProcessor.Web.Helpers;
+ using System.Collections.Concurrent;
+ using System.Threading.Tasks;
+ using System.Threading;
#endregion
///
@@ -46,18 +49,46 @@ namespace ImageProcessor.Web.HttpModules
/// The assembly version.
///
private static readonly string AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
+
+ ///
+ /// The thread safe fifo queue.
+ ///
+ private static ConcurrentQueue imageOperations;
+
+ ///
+ /// A value indicating whether the application has started.
+ ///
+ private static bool hasAppStarted = false;
#endregion
#region IHttpModule Members
///
/// Initializes a module and prepares it to handle requests.
///
- /// An that provides access to the methods, properties, and events common to all application objects within an ASP.NET application
+ ///
+ /// An that provides
+ /// access to the methods, properties, and events common to all
+ /// application objects within an ASP.NET application
+ ///
public void Init(HttpApplication context)
{
- DiskCache.CreateCacheDirectories();
- context.BeginRequest += this.ContextBeginRequest;
+ if (!hasAppStarted)
+ {
+ lock (SyncRoot)
+ {
+ if (!hasAppStarted)
+ {
+ imageOperations = new ConcurrentQueue();
+ DiskCache.CreateCacheDirectories();
+ hasAppStarted = true;
+ }
+ }
+ }
+
+ context.AddOnBeginRequestAsync(OnBeginAsync, OnEndAsync);
+ //context.BeginRequest += this.ContextBeginRequest;
context.PreSendRequestHeaders += this.ContextPreSendRequestHeaders;
+
}
///
@@ -69,15 +100,119 @@ namespace ImageProcessor.Web.HttpModules
}
#endregion
+ ///
+ /// The that starts asynchronous processing
+ /// of the .
+ ///
+ /// The source of the event.
+ ///
+ /// An EventArgs that contains
+ /// the event data.
+ ///
+ ///
+ /// The delegate to call when the asynchronous method call is complete.
+ /// If cb is null, the delegate is not called.
+ ///
+ ///
+ /// Any additional data needed to process the request.
+ ///
+ ///
+ IAsyncResult OnBeginAsync(object sender, EventArgs e, AsyncCallback cb, object extraData)
+ {
+ HttpContext context = ((HttpApplication)sender).Context;
+ EnqueueDelegate enqueueDelegate = new EnqueueDelegate(Enqueue);
+
+ return enqueueDelegate.BeginInvoke(context, cb, extraData);
+
+ }
+
+ ///
+ /// The method that handles asynchronous events such as application events.
+ ///
+ ///
+ /// The that is the result of the
+ /// operation.
+ ///
+ public void OnEndAsync(IAsyncResult result)
+ {
+ // An action to consume the ConcurrentQueue.
+ Action action = () =>
+ {
+ Action op;
+
+ while (imageOperations.TryDequeue(out op))
+ {
+ op();
+ }
+ };
+
+ // Start 4 concurrent consuming actions.
+ Parallel.Invoke(action, action, action, action);
+ }
+
+ ///
+ /// The delegate void representing the Enqueue method.
+ ///
+ ///
+ /// the HttpContext object that provides
+ /// references to the intrinsic server objects
+ ///
+ private delegate void EnqueueDelegate(HttpContext context);
+
+ ///
+ /// Adds the method to the queue.
+ ///
+ ///
+ /// the HttpContext object that provides
+ /// references to the intrinsic server objects
+ ///
+ private void Enqueue(HttpContext context)
+ {
+ imageOperations.Enqueue(() => ProcessImage(context));
+ }
+
///
/// Occurs as the first event in the HTTP pipeline chain of execution when ASP.NET responds to a request.
///
/// The source of the event.
/// An EventArgs that contains the event data.
private void ContextBeginRequest(object sender, EventArgs e)
+ {
+ HttpContext context = ((HttpApplication)sender).Context;
+ imageOperations.Enqueue(() => ProcessImage(context));
+ }
+
+ ///
+ /// Occurs just before ASP.NET send HttpHeaders to the client.
+ ///
+ /// The source of the event.
+ /// An EventArgs that contains the event data.
+ private void ContextPreSendRequestHeaders(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
+ object responseTypeObject = context.Items[CachedResponseTypeKey];
+
+ if (responseTypeObject != null)
+ {
+ string responseType = (string)responseTypeObject;
+
+ this.SetHeaders(context, responseType);
+
+ context.Items[CachedResponseTypeKey] = null;
+ }
+ }
+
+ #region Private
+ ///
+ /// Processes the image.
+ ///
+ ///
+ /// the HttpContext object that provides
+ /// references to the intrinsic server objects
+ ///
+ private void ProcessImage(HttpContext context)
+ {
// Is this a remote file.
bool isRemote = context.Request.Path.Equals(RemotePrefix, StringComparison.OrdinalIgnoreCase);
string path = string.Empty;
@@ -133,43 +268,43 @@ namespace ImageProcessor.Web.HttpModules
{
if (responseStream != null)
{
- lock (SyncRoot)
- {
- // Trim the cache.
- DiskCache.TrimCachedFolders();
+ //lock (SyncRoot)
+ //{
+ // Trim the cache.
+ DiskCache.TrimCachedFolders();
- responseStream.CopyTo(memoryStream);
+ responseStream.CopyTo(memoryStream);
- imageFactory.Load(memoryStream)
- .AddQueryString(queryString)
- .Format(ImageUtils.GetImageFormat(imageName))
- .AutoProcess().Save(cachedPath);
+ imageFactory.Load(memoryStream)
+ .AddQueryString(queryString)
+ .Format(ImageUtils.GetImageFormat(imageName))
+ .AutoProcess().Save(cachedPath);
- // Ensure that the LastWriteTime property of the source and cached file match.
- DateTime dateTime = DiskCache.SetCachedLastWriteTime(path, cachedPath, true);
+ // Ensure that the LastWriteTime property of the source and cached file match.
+ DateTime dateTime = DiskCache.SetCachedLastWriteTime(path, cachedPath, true);
- // Add to the cache.
- DiskCache.AddImageToCache(cachedPath, dateTime);
- }
+ // Add to the cache.
+ DiskCache.AddImageToCache(cachedPath, dateTime);
+ //}
}
}
}
}
else
{
- lock (SyncRoot)
- {
- // Trim the cache.
- DiskCache.TrimCachedFolders();
+ //lock (SyncRoot)
+ //{
+ // Trim the cache.
+ DiskCache.TrimCachedFolders();
- imageFactory.Load(fullPath).AutoProcess().Save(cachedPath);
+ imageFactory.Load(fullPath).AutoProcess().Save(cachedPath);
- // Ensure that the LastWriteTime property of the source and cached file match.
- DateTime dateTime = DiskCache.SetCachedLastWriteTime(path, cachedPath, false);
+ // Ensure that the LastWriteTime property of the source and cached file match.
+ DateTime dateTime = DiskCache.SetCachedLastWriteTime(path, cachedPath, false);
- // Add to the cache.
- DiskCache.AddImageToCache(cachedPath, dateTime);
- }
+ // Add to the cache.
+ DiskCache.AddImageToCache(cachedPath, dateTime);
+ //}
}
}
}
@@ -182,28 +317,6 @@ namespace ImageProcessor.Web.HttpModules
}
}
- ///
- /// Occurs just before ASP.NET send HttpHeaders to the client.
- ///
- /// The source of the event.
- /// An EventArgs that contains the event data.
- private void ContextPreSendRequestHeaders(object sender, EventArgs e)
- {
- HttpContext context = ((HttpApplication)sender).Context;
-
- object responseTypeObject = context.Items[CachedResponseTypeKey];
-
- if (responseTypeObject != null)
- {
- string responseType = (string)responseTypeObject;
-
- this.SetHeaders(context, responseType);
-
- context.Items[CachedResponseTypeKey] = null;
- }
- }
-
- #region Private
///
/// returns a value indicating whether a file exists.
///
diff --git a/src/ImageProcessor.Web/ImageFactoryExtensions.cs b/src/ImageProcessor.Web/ImageFactoryExtensions.cs
index a66bfeb432..93e383fae1 100644
--- a/src/ImageProcessor.Web/ImageFactoryExtensions.cs
+++ b/src/ImageProcessor.Web/ImageFactoryExtensions.cs
@@ -19,6 +19,11 @@ namespace ImageProcessor.Web
///
public static class ImageFactoryExtensions
{
+ ///
+ /// The object to lock against.
+ ///
+ private static readonly object SyncRoot = new object();
+
///
/// Auto processes image files based on any query string parameters added to the image path.
///
@@ -33,17 +38,20 @@ namespace ImageProcessor.Web
{
if (factory.ShouldProcess)
{
- // Get a list of all graphics processors that have parsed and matched the querystring.
- List list =
- ImageProcessorConfig.Instance.GraphicsProcessors
- .Where(x => x.MatchRegexIndex(factory.QueryString) != int.MaxValue)
- .OrderBy(y => y.SortOrder)
- .ToList();
-
- // Loop through and process the image.
- foreach (IGraphicsProcessor graphicsProcessor in list)
+ lock (SyncRoot)
{
- factory.Image = graphicsProcessor.ProcessImage(factory);
+ // Get a list of all graphics processors that have parsed and matched the querystring.
+ List list =
+ ImageProcessorConfig.Instance.GraphicsProcessors
+ .Where(x => x.MatchRegexIndex(factory.QueryString) != int.MaxValue)
+ .OrderBy(y => y.SortOrder)
+ .ToList();
+
+ // Loop through and process the image.
+ foreach (IGraphicsProcessor graphicsProcessor in list)
+ {
+ factory.Image = graphicsProcessor.ProcessImage(factory);
+ }
}
}