Browse Source

New cache cleanup mechanism

Former-commit-id: f3dd407dab25fafd11b7fe3f37a152fcae2a343f
pull/17/head
James South 12 years ago
parent
commit
368fa23fa2
  1. 15
      src/ImageProcessor.Web/NET4/ImageProcessor.Web.csproj
  2. 34
      src/ImageProcessor.Web/NET45/Caching/CacheIndexer.cs
  3. 32
      src/ImageProcessor.Web/NET45/Caching/CleanupImage.cs
  4. 97
      src/ImageProcessor.Web/NET45/Caching/DiskCache.cs
  5. 12
      src/ImageProcessor.Web/NET45/Caching/MemCache.cs
  6. 42
      src/ImageProcessor.Web/NET45/Caching/SQLContext.cs
  7. 36
      src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs
  8. 5
      src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj
  9. 4
      src/ImageProcessor.Web/NET45/Properties/AssemblyInfo.cs
  10. 4
      src/ImageProcessor/Properties/AssemblyInfo.cs
  11. 4
      src/TestWebsites/NET45/Test_Website_NET45/Test_Website_NET45.csproj
  12. 5
      src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml
  13. 1
      src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Png.cshtml

15
src/ImageProcessor.Web/NET4/ImageProcessor.Web.csproj

@ -45,6 +45,7 @@
<Reference Include="Community.CsharpSqlite.SQLiteClient"> <Reference Include="Community.CsharpSqlite.SQLiteClient">
<HintPath>..\..\packages\Csharp-Sqlite.3.7.7.1\lib\net40\Community.CsharpSqlite.SQLiteClient.dll</HintPath> <HintPath>..\..\packages\Csharp-Sqlite.3.7.7.1\lib\net40\Community.CsharpSqlite.SQLiteClient.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\Microsoft.Bcl.Async.1.0.16\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> <HintPath>..\..\packages\Microsoft.Bcl.Async.1.0.16\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
@ -74,15 +75,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\NET45\Caching\CachedImage.cs" /> <Compile Include="..\NET45\Caching\CachedImage.cs" />
<Compile Include="..\NET45\Caching\CacheManager.cs"> <Compile Include="..\NET45\Caching\CacheIndexer.cs">
<Link>CacheManager.cs</Link> <Link>CacheIndexer.cs</Link>
</Compile>
<Compile Include="..\NET45\Caching\CleanupImage.cs">
<Link>CleanupImage.cs</Link>
</Compile> </Compile>
<Compile Include="..\NET45\Caching\DiskCache.cs" /> <Compile Include="..\NET45\Caching\DiskCache.cs" />
<Compile Include="..\NET45\Caching\MemoryCache.cs"> <Compile Include="..\NET45\Caching\MemCache.cs">
<Link>MemoryCache.cs</Link> <Link>MemCache.cs</Link>
</Compile> </Compile>
<Compile Include="..\NET45\Caching\SQLContext.cs" /> <Compile Include="..\NET45\Caching\SQLContext.cs" />
<Compile Include="..\NET45\Config\ImageCacheSection.cs" /> <Compile Include="..\NET45\Config\ImageCacheSection.cs" />
@ -93,6 +91,9 @@
<Compile Include="..\NET45\Helpers\TaskHelpers.cs" /> <Compile Include="..\NET45\Helpers\TaskHelpers.cs" />
<Compile Include="..\NET45\HttpModules\ImageProcessingModule.cs" /> <Compile Include="..\NET45\HttpModules\ImageProcessingModule.cs" />
<Compile Include="..\NET45\ImageFactoryExtensions.cs" /> <Compile Include="..\NET45\ImageFactoryExtensions.cs" />
<Compile Include="..\NET45\Preset.cs">
<Link>Preset.cs</Link>
</Compile>
<Compile Include="..\NET45\Properties\AssemblyInfo.cs" /> <Compile Include="..\NET45\Properties\AssemblyInfo.cs" />
<Compile Include="..\NET45\SQLite.cs" /> <Compile Include="..\NET45\SQLite.cs" />
<Compile Include="..\NET45\SQLiteAsync.cs" /> <Compile Include="..\NET45\SQLiteAsync.cs" />

34
src/ImageProcessor.Web/NET45/Caching/MemoryCache.cs → src/ImageProcessor.Web/NET45/Caching/CacheIndexer.cs

@ -1,10 +1,10 @@
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
// <copyright file="MemoryCache.cs" company="James South"> // <copyright file="CacheIndexer.cs" company="James South">
// Copyright (c) James South. // Copyright (c) James South.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
// <summary> // <summary>
// // Represents an in memory collection of keys and values whose operations are concurrent.
// </summary> // </summary>
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
@ -18,15 +18,15 @@ namespace ImageProcessor.Web.Caching
/// <summary> /// <summary>
/// Represents an in memory collection of keys and values whose operations are concurrent. /// Represents an in memory collection of keys and values whose operations are concurrent.
/// </summary> /// </summary>
internal sealed class MemoryCache internal sealed class CacheIndexer
{ {
#region Fields #region Fields
/// <summary> /// <summary>
/// A new instance Initializes a new instance of the <see cref="T:ImageProcessor.Web.Caching.MemoryCache"/> class. /// A new instance Initializes a new instance of the <see cref="T:ImageProcessor.Web.Caching.CacheIndexer"/> class.
/// initialized lazily. /// initialized lazily.
/// </summary> /// </summary>
private static readonly Lazy<MemoryCache> Lazy = private static readonly Lazy<CacheIndexer> Lazy =
new Lazy<MemoryCache>(() => new MemoryCache()); new Lazy<CacheIndexer>(() => new CacheIndexer());
/// <summary> /// <summary>
/// The object to lock against. /// The object to lock against.
@ -36,19 +36,19 @@ namespace ImageProcessor.Web.Caching
#region Constructors #region Constructors
/// <summary> /// <summary>
/// Prevents a default instance of the <see cref="T:ImageProcessor.Web.Caching.MemoryCache"/> class /// Prevents a default instance of the <see cref="T:ImageProcessor.Web.Caching.CacheIndexer"/> class
/// from being created. /// from being created.
/// </summary> /// </summary>
private MemoryCache() private CacheIndexer()
{ {
this.LoadCache(); this.LoadCache();
} }
#endregion #endregion
/// <summary> /// <summary>
/// Gets the current instance of the <see cref="T:ImageProcessor.Web.Caching.MemoryCache"/> class. /// Gets the current instance of the <see cref="T:ImageProcessor.Web.Caching.CacheIndexer"/> class.
/// </summary> /// </summary>
public static MemoryCache Instance public static CacheIndexer Instance
{ {
get get
{ {
@ -64,12 +64,12 @@ namespace ImageProcessor.Web.Caching
/// The key of the value to get. /// The key of the value to get.
/// </param> /// </param>
/// <returns> /// <returns>
/// The <see cref="CachedImage"/> matching the given key if the <see cref="MemoryCache"/> contains an element with /// The <see cref="CachedImage"/> matching the given key if the <see cref="CacheIndexer"/> contains an element with
/// the specified key; otherwise, null. /// the specified key; otherwise, null.
/// </returns> /// </returns>
public async Task<CachedImage> GetValueAsync(string key) public async Task<CachedImage> GetValueAsync(string key)
{ {
CachedImage cachedImage = (CachedImage)CacheManager.GetItem(key); CachedImage cachedImage = (CachedImage)MemCache.GetItem(key);
if (cachedImage == null) if (cachedImage == null)
{ {
@ -77,7 +77,7 @@ namespace ImageProcessor.Web.Caching
if (cachedImage != null) if (cachedImage != null)
{ {
CacheManager.AddItem(key, cachedImage); MemCache.AddItem(key, cachedImage);
} }
} }
@ -91,14 +91,14 @@ namespace ImageProcessor.Web.Caching
/// The key of the item to remove. /// The key of the item to remove.
/// </param> /// </param>
/// <returns> /// <returns>
/// true if the <see cref="MemoryCache"/> removes an element with /// true if the <see cref="CacheIndexer"/> removes an element with
/// the specified key; otherwise, false. /// the specified key; otherwise, false.
/// </returns> /// </returns>
public async Task<bool> RemoveAsync(string key) public async Task<bool> RemoveAsync(string key)
{ {
if (await this.SaveCacheAsync(key, null, true) > 0) if (await this.SaveCacheAsync(key, null, true) > 0)
{ {
CacheManager.RemoveItem(key); MemCache.RemoveItem(key);
return true; return true;
} }
@ -122,7 +122,7 @@ namespace ImageProcessor.Web.Caching
// Add the CachedImage. // Add the CachedImage.
if (await this.SaveCacheAsync(key, cachedImage, false) > 0) if (await this.SaveCacheAsync(key, cachedImage, false) > 0)
{ {
CacheManager.AddItem(key, cachedImage); MemCache.AddItem(key, cachedImage);
} }
return cachedImage; return cachedImage;
@ -130,7 +130,7 @@ namespace ImageProcessor.Web.Caching
#endregion #endregion
/// <summary> /// <summary>
/// Saves the in memory cache to the file-system. /// Saves the image to the file-system cache.
/// </summary> /// </summary>
/// <param name="key"> /// <param name="key">
/// The key. /// The key.

32
src/ImageProcessor.Web/NET45/Caching/CleanupImage.cs

@ -1,32 +0,0 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CleanupImage.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Describes a cached image for cleanup.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Caching
{
#region Using
using System;
#endregion
/// <summary>
/// Describes a cached image for cleanup
/// </summary>
public sealed class CleanupImage
{
/// <summary>
/// Gets or sets the value of the cached image.
/// </summary>
public string Path { get; set; }
/// <summary>
/// Gets or sets when the cached image should expire from the cache.
/// </summary>
public DateTime ExpiresUtc { get; set; }
}
}

97
src/ImageProcessor.Web/NET45/Caching/DiskCache.cs

@ -12,11 +12,11 @@ namespace ImageProcessor.Web.Caching
{ {
#region Using #region Using
using System; using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web; using System.Web;
using System.Web.Hosting; using System.Web.Hosting;
@ -47,17 +47,7 @@ namespace ImageProcessor.Web.Caching
/// <see cref="http://stackoverflow.com/questions/115882/how-do-you-deal-with-lots-of-small-files"/> /// <see cref="http://stackoverflow.com/questions/115882/how-do-you-deal-with-lots-of-small-files"/>
/// <see cref="http://stackoverflow.com/questions/1638219/millions-of-small-graphics-files-and-how-to-overcome-slow-file-system-access-on"/> /// <see cref="http://stackoverflow.com/questions/1638219/millions-of-small-graphics-files-and-how-to-overcome-slow-file-system-access-on"/>
/// </remarks> /// </remarks>
private const int MaxFilesCount = 100; private const int MaxFilesCount = 50;
/// <summary>
/// The regular expression to search strings for valid subfolder names.
/// We're specifically not using a shorter regex as we need to be able to iterate through
/// each match group.
/// </summary>
private static readonly Regex SubFolderRegex =
new Regex(
@"(\/([a-z]|[0-9])\/(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|0|1|2|3|4|5|6|7|8|9)\/)",
RegexOptions.Compiled);
/// <summary> /// <summary>
/// The absolute path to virtual cache path on the server. /// The absolute path to virtual cache path on the server.
@ -171,7 +161,7 @@ namespace ImageProcessor.Web.Caching
ExpiresUtc = expires ExpiresUtc = expires
}; };
await MemoryCache.Instance.AddAsync(key, cachedImage); await CacheIndexer.Instance.AddAsync(key, cachedImage);
} }
/// <summary> /// <summary>
@ -188,7 +178,7 @@ namespace ImageProcessor.Web.Caching
if (this.isRemote) if (this.isRemote)
{ {
cachedImage = await MemoryCache.Instance.GetValueAsync(key); cachedImage = await CacheIndexer.Instance.GetValueAsync(key);
if (cachedImage != null) if (cachedImage != null)
{ {
@ -197,7 +187,7 @@ namespace ImageProcessor.Web.Caching
if (cachedImage.ExpiresUtc < DateTime.UtcNow.AddDays(-MaxFileCachedDuration) if (cachedImage.ExpiresUtc < DateTime.UtcNow.AddDays(-MaxFileCachedDuration)
|| cachedImage.MaxAge != MaxFileCachedDuration) || cachedImage.MaxAge != MaxFileCachedDuration)
{ {
if (await MemoryCache.Instance.RemoveAsync(key)) if (await CacheIndexer.Instance.RemoveAsync(key))
{ {
isUpdated = true; isUpdated = true;
} }
@ -212,7 +202,7 @@ namespace ImageProcessor.Web.Caching
else else
{ {
// Test now for locally requested files. // Test now for locally requested files.
cachedImage = await MemoryCache.Instance.GetValueAsync(key); cachedImage = await CacheIndexer.Instance.GetValueAsync(key);
if (cachedImage != null) if (cachedImage != null)
{ {
@ -226,7 +216,7 @@ namespace ImageProcessor.Web.Caching
|| cachedImage.ExpiresUtc < DateTime.UtcNow.AddDays(-MaxFileCachedDuration) || cachedImage.ExpiresUtc < DateTime.UtcNow.AddDays(-MaxFileCachedDuration)
|| cachedImage.MaxAge != MaxFileCachedDuration) || cachedImage.MaxAge != MaxFileCachedDuration)
{ {
if (await MemoryCache.Instance.RemoveAsync(key)) if (await CacheIndexer.Instance.RemoveAsync(key))
{ {
isUpdated = true; isUpdated = true;
} }
@ -254,7 +244,7 @@ namespace ImageProcessor.Web.Caching
string key = Path.GetFileNameWithoutExtension(this.CachedPath); string key = Path.GetFileNameWithoutExtension(this.CachedPath);
DateTime dateTime = DateTime.UtcNow; DateTime dateTime = DateTime.UtcNow;
CachedImage cachedImage = await MemoryCache.Instance.GetValueAsync(key); CachedImage cachedImage = await CacheIndexer.Instance.GetValueAsync(key);
if (cachedImage != null) if (cachedImage != null)
{ {
@ -277,15 +267,17 @@ namespace ImageProcessor.Web.Caching
} }
/// <summary> /// <summary>
/// Purges any files from the file-system cache in the given folders. /// Trims a cached folder ensuring that it does not exceed the maximum file count.
/// </summary> /// </summary>
/// <param name="path">
/// The path to the folder.
/// </param>
/// <returns> /// <returns>
/// The <see cref="T:System.Threading.Tasks.Task"/>. /// The <see cref="T:System.Threading.Tasks.Task"/>.
/// </returns> /// </returns>
internal async Task TrimCachedFoldersAsync() internal async Task TrimCachedFolderAsync(string path)
{ {
// Create Action delegate for TrimCachedFolders. await TaskHelpers.Run(() => this.TrimCachedFolder(path));
await TaskHelpers.Run(this.TrimCachedFolders);
} }
#endregion #endregion
@ -325,50 +317,42 @@ namespace ImageProcessor.Web.Caching
} }
/// <summary> /// <summary>
/// Purges any files from the file-system cache in the given folders. /// Trims a cached folder ensuring that it does not exceed the maximum file count.
/// </summary> /// </summary>
private async void TrimCachedFolders() /// <param name="path">
/// The path to the folder.
/// </param>
private async void TrimCachedFolder(string path)
{ {
// Group each cache folder and clear any expired items or any that exceed // ReSharper disable once AssignNullToNotNullAttribute
// the maximum allowable count. DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetDirectoryName(path));
var groups = SQLContext.GetImagesForCleanup() IEnumerable<FileInfo> files = directoryInfo.EnumerateFiles().OrderBy(f => f.LastWriteTimeUtc);
.GroupBy(x => SubFolderRegex.Match(x.Path).Value) int count = files.Count();
.Where(g => g.Count() > MaxFilesCount);
foreach (var group in groups) foreach (FileInfo fileInfo in files)
{ {
int groupCount = group.Count(); try
foreach (CleanupImage image in group.OrderBy(x => x.ExpiresUtc))
{ {
// If the group count is equal to the max count minus 1 then we know we // If the group count is equal to the max count minus 1 then we know we
// are counting down from a full directory not simply clearing out // have reduced the number of items below the maximum allowed.
// expired items. if (count <= MaxFilesCount - 1)
if (groupCount <= MaxFilesCount - 1
&& image.ExpiresUtc >= DateTime.UtcNow.AddDays(-MaxFileCachedDuration))
{ {
break; break;
} }
try // Remove from the cache and delete each CachedImage.
string key = Path.GetFileNameWithoutExtension(fileInfo.Name);
if (await CacheIndexer.Instance.RemoveAsync(key))
{ {
// Remove from the cache and delete each CachedImage. fileInfo.Delete();
FileInfo fileInfo = new FileInfo(image.Path); count -= 1;
string key = Path.GetFileNameWithoutExtension(fileInfo.Name);
if (await MemoryCache.Instance.RemoveAsync(key))
{
fileInfo.Delete();
groupCount -= 1;
}
}
// ReSharper disable EmptyGeneralCatchClause
catch
// ReSharper restore EmptyGeneralCatchClause
{
// Do nothing; skip to the next file.
} }
} }
// ReSharper disable once EmptyGeneralCatchClause
catch
{
// Do nothing; skip to the next file.
}
} }
} }
@ -376,8 +360,8 @@ namespace ImageProcessor.Web.Caching
/// Gets the full transformed cached path for the image. /// Gets the full transformed cached path for the image.
/// The images are stored in paths that are based upon the sha1 of their full request path /// The images are stored in paths that are based upon the sha1 of their full request path
/// taking the individual characters of the hash to determine their location. /// taking the individual characters of the hash to determine their location.
/// This allows us to store 40 folders within 40 folders giving us a total of 3.0223145e+64 potential images. /// This allows us to store millions of images.
/// Answers on a post card if you can figure out a way to store their details in a db for fast recovery. /// Answers on a post card if you can figure out a way to store that many details in a db for fast recovery.
/// </summary> /// </summary>
/// <returns>The full cached path for the image.</returns> /// <returns>The full cached path for the image.</returns>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")] [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
@ -394,7 +378,8 @@ namespace ImageProcessor.Web.Caching
string fallbackExtension = this.imageName.Substring(this.imageName.LastIndexOf(".", StringComparison.Ordinal) + 1); string fallbackExtension = this.imageName.Substring(this.imageName.LastIndexOf(".", StringComparison.Ordinal) + 1);
string encryptedName = this.fullPath.ToSHA1Fingerprint(); string encryptedName = this.fullPath.ToSHA1Fingerprint();
string pathFromKey = string.Join("\\", encryptedName.ToCharArray()); // Collision rate of about 1 in 1000 for the folder structure.
string pathFromKey = string.Join("\\", encryptedName.ToCharArray().Take(5));
string cachedFileName = string.Format( string cachedFileName = string.Format(
"{0}.{1}", "{0}.{1}",

12
src/ImageProcessor.Web/NET45/Caching/CacheManager.cs → src/ImageProcessor.Web/NET45/Caching/MemCache.cs

@ -1,10 +1,10 @@
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
// <copyright file="CacheManager.cs" company="James South"> // <copyright file="MemCache.cs" company="James South">
// Copyright (c) James South. // Copyright (c) James South.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
// <summary> // <summary>
// Encapsulates methods that allow the caching and retrieval of objects. // Encapsulates methods that allow the caching and retrieval of objects from the in memory cache.
// </summary> // </summary>
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
@ -17,15 +17,15 @@ namespace ImageProcessor.Web.Caching
#endregion #endregion
/// <summary> /// <summary>
/// Encapsulates methods that allow the caching and retrieval of objects. /// Encapsulates methods that allow the caching and retrieval of objects from the in memory cache.
/// </summary> /// </summary>
public static class CacheManager internal static class MemCache
{ {
#region Fields #region Fields
/// <summary> /// <summary>
/// The cache /// The cache
/// </summary> /// </summary>
private static readonly ObjectCache Cache = System.Runtime.Caching.MemoryCache.Default; private static readonly ObjectCache Cache = MemoryCache.Default;
/// <summary> /// <summary>
/// An internal list of cache keys to allow bulk removal. /// An internal list of cache keys to allow bulk removal.
@ -99,8 +99,6 @@ namespace ImageProcessor.Web.Caching
return Cache.Get(key, regionName); return Cache.Get(key, regionName);
} }
//public static bool
/// <summary> /// <summary>
/// Updates an item to the cache. /// Updates an item to the cache.
/// </summary> /// </summary>

42
src/ImageProcessor.Web/NET45/Caching/SQLContext.cs

@ -1,24 +1,21 @@
// ----------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
// <copyright file="SQLContext.cs" company="James South"> // <copyright file="SQLContext.cs" company="James South">
// Copyright (c) James South. // Copyright (c) James South.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
// ----------------------------------------------------------------------- // <summary>
// Provides a wrapper for the SQLite functionality.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Caching namespace ImageProcessor.Web.Caching
{ {
#region Using #region Using
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web.Hosting; using System.Web.Hosting;
using ImageProcessor.Web.Config; using ImageProcessor.Web.Config;
using ImageProcessor.Web.Helpers;
using SQLite; using SQLite;
#endregion #endregion
/// <summary> /// <summary>
@ -79,31 +76,6 @@ namespace ImageProcessor.Web.Caching
} }
} }
/// <summary>
/// Gets all the images from the database.
/// </summary>
/// <returns>
/// The <see cref="System.Collections.Generic.List{CleanupImage}"/>.
/// </returns>
internal static List<CleanupImage> GetImagesForCleanup()
{
try
{
List<CleanupImage> images;
using (SQLiteConnection connection = new SQLiteConnection(ConnectionString))
{
images = connection.Query<CleanupImage>("SELECT Path,ExpiresUtc FROM CachedImage");
}
return images;
}
catch
{
return new List<CleanupImage>();
}
}
/// <summary> /// <summary>
/// Gets a cached image from the database. /// Gets a cached image from the database.
/// </summary> /// </summary>

36
src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs

@ -20,12 +20,10 @@ namespace ImageProcessor.Web.HttpModules
using System.Security; using System.Security;
using System.Security.Permissions; using System.Security.Permissions;
using System.Security.Principal; using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web; using System.Web;
using System.Web.Hosting; using System.Web.Hosting;
using System.Web.Security; using System.Web.Security;
using ImageProcessor.Helpers.Extensions; using ImageProcessor.Helpers.Extensions;
using ImageProcessor.Imaging; using ImageProcessor.Imaging;
using ImageProcessor.Web.Caching; using ImageProcessor.Web.Caching;
@ -53,16 +51,6 @@ namespace ImageProcessor.Web.HttpModules
/// The assembly version. /// The assembly version.
/// </summary> /// </summary>
private static readonly string AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); private static readonly string AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
/// <summary>
/// The value that acts as a basis to check that the startup code has only been ran once.
/// </summary>
private static int initCheck;
/// <summary>
/// A value indicating whether the application has started.
/// </summary>
private readonly bool hasModuleInitialized = initCheck == 1;
#endregion #endregion
#region IHttpModule Members #region IHttpModule Members
@ -76,12 +64,6 @@ namespace ImageProcessor.Web.HttpModules
/// </param> /// </param>
public void Init(HttpApplication context) public void Init(HttpApplication context)
{ {
if (!this.hasModuleInitialized)
{
Interlocked.CompareExchange(ref initCheck, 1, 0);
// DiskCache.CreateDirectories();
}
#if NET45 #if NET45
EventHandlerTaskAsyncHelper wrapper = new EventHandlerTaskAsyncHelper(this.PostAuthorizeRequest); EventHandlerTaskAsyncHelper wrapper = new EventHandlerTaskAsyncHelper(this.PostAuthorizeRequest);
@ -288,6 +270,8 @@ namespace ImageProcessor.Web.HttpModules
// Only process if the file has been updated. // Only process if the file has been updated.
if (isNewOrUpdated) if (isNewOrUpdated)
{ {
string cachedPath = cache.CachedPath;
// Process the image. // Process the image.
using (ImageFactory imageFactory = new ImageFactory()) using (ImageFactory imageFactory = new ImageFactory())
{ {
@ -308,21 +292,21 @@ namespace ImageProcessor.Web.HttpModules
{ {
if (responseStream != null) if (responseStream != null)
{ {
// Trim the cache.
await cache.TrimCachedFoldersAsync();
responseStream.CopyTo(memoryStream); responseStream.CopyTo(memoryStream);
imageFactory.Load(memoryStream) imageFactory.Load(memoryStream)
.AddQueryString(queryString) .AddQueryString(queryString)
.Format(ImageUtils.GetImageFormat(imageName)) .Format(ImageUtils.GetImageFormat(imageName))
.AutoProcess().Save(cache.CachedPath); .AutoProcess().Save(cachedPath);
// Ensure that the LastWriteTime property of the source and cached file match. // Ensure that the LastWriteTime property of the source and cached file match.
DateTime dateTime = await cache.SetCachedLastWriteTimeAsync(); DateTime dateTime = await cache.SetCachedLastWriteTimeAsync();
// Add to the cache. // Add to the cache.
await cache.AddImageToCacheAsync(dateTime); await cache.AddImageToCacheAsync(dateTime);
// Trim the cache.
await cache.TrimCachedFolderAsync(cachedPath);
} }
} }
} }
@ -330,16 +314,16 @@ namespace ImageProcessor.Web.HttpModules
} }
else else
{ {
// Trim the cache. imageFactory.Load(fullPath).AutoProcess().Save(cachedPath);
await cache.TrimCachedFoldersAsync();
imageFactory.Load(fullPath).AutoProcess().Save(cache.CachedPath);
// Ensure that the LastWriteTime property of the source and cached file match. // Ensure that the LastWriteTime property of the source and cached file match.
DateTime dateTime = await cache.SetCachedLastWriteTimeAsync(); DateTime dateTime = await cache.SetCachedLastWriteTimeAsync();
// Add to the cache. // Add to the cache.
await cache.AddImageToCacheAsync(dateTime); await cache.AddImageToCacheAsync(dateTime);
// Trim the cache.
await cache.TrimCachedFolderAsync(cachedPath);
} }
} }
} }

5
src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj

@ -50,10 +50,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Caching\CachedImage.cs" /> <Compile Include="Caching\CachedImage.cs" />
<Compile Include="Caching\CacheManager.cs" /> <Compile Include="Caching\MemCache.cs" />
<Compile Include="Caching\CleanupImage.cs" />
<Compile Include="Caching\DiskCache.cs" /> <Compile Include="Caching\DiskCache.cs" />
<Compile Include="Caching\MemoryCache.cs" /> <Compile Include="Caching\CacheIndexer.cs" />
<Compile Include="Caching\SQLContext.cs" /> <Compile Include="Caching\SQLContext.cs" />
<Compile Include="Config\ImageCacheSection.cs" /> <Compile Include="Config\ImageCacheSection.cs" />
<Compile Include="Config\ImageProcessingSection.cs" /> <Compile Include="Config\ImageProcessingSection.cs" />

4
src/ImageProcessor.Web/NET45/Properties/AssemblyInfo.cs

@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
[assembly: AssemblyVersion("2.3.0.6")] [assembly: AssemblyVersion("2.4.0.0")]
[assembly: AssemblyFileVersion("2.3.0.6")] [assembly: AssemblyFileVersion("2.4.0.0")]

4
src/ImageProcessor/Properties/AssemblyInfo.cs

@ -32,6 +32,6 @@ using System.Security;
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
[assembly: AssemblyVersion("1.7.1.1")] [assembly: AssemblyVersion("1.8.0.0")]
[assembly: AssemblyFileVersion("1.7.1.1")] [assembly: AssemblyFileVersion("1.8.0.0")]

4
src/TestWebsites/NET45/Test_Website_NET45/Test_Website_NET45.csproj

@ -193,7 +193,9 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="config\imageprocessor\processing.config" /> <Content Include="config\imageprocessor\processing.config">
<SubType>Designer</SubType>
</Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="config\imageprocessor\security.config" /> <Content Include="config\imageprocessor\security.config" />

5
src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml

@ -141,7 +141,7 @@
<article> <article>
<h1>Color Profiles</h1> <h1>Color Profiles</h1>
<section> @* <section>
<div class="row"> <div class="row">
<div class="col-s-6"> <div class="col-s-6">
<h2>CMYK original jpg</h2> <h2>CMYK original jpg</h2>
@ -151,9 +151,8 @@
<h2>sRGB original jpg</h2> <h2>sRGB original jpg</h2>
<img src="/images/srgb.jpg?" width="400" /> <img src="/images/srgb.jpg?" width="400" />
</div> </div>
</div> </div>
</section> </section>*@
<section> <section>
<div class="row"> <div class="row">
<div class="col-s-6"> <div class="col-s-6">

1
src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Png.cshtml

@ -8,7 +8,6 @@
<div class="col-s-6"> <div class="col-s-6">
<h2>Resized</h2> <h2>Resized</h2>
<img src="/images/Penguins.png?width=300" /> <img src="/images/Penguins.png?width=300" />
<img src="/gifts/cmyk.png?x=300" />
</div> </div>
<div class="col-s-6"> <div class="col-s-6">
<h2>Cropped </h2> <h2>Cropped </h2>

Loading…
Cancel
Save