Browse Source

Merge remote-tracking branch 'origin/dev' into V2

Former-commit-id: bd017437e42a5c69303ea2a18e333dc70dca2d39
af/merge-core
James South 12 years ago
parent
commit
fd6497dfb8
  1. 77
      src/ImageProcessor.Web/NET45/Caching/DiskCache.cs
  2. 61
      src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs
  3. 2
      src/ImageProcessor/Imaging/Formats/GifFormat.cs
  4. 8
      src/TestWebsites/NET45/Test_Website_NET45/Web.config

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

@ -49,14 +49,14 @@ namespace ImageProcessor.Web.Caching
private const int MaxFilesCount = 100; private const int MaxFilesCount = 100;
/// <summary> /// <summary>
/// The absolute path to virtual cache path on the server. /// The virtual cache path.
/// </summary> /// </summary>
private static readonly string AbsoluteCachePath = HostingEnvironment.MapPath(ImageProcessorConfiguration.Instance.VirtualCachePath); private static readonly string VirtualCachePath = ImageProcessorConfiguration.Instance.VirtualCachePath;
/// <summary> /// <summary>
/// The request for the image. /// The absolute path to virtual cache path on the server.
/// </summary> /// </summary>
private readonly HttpRequest request; private static readonly string AbsoluteCachePath = HostingEnvironment.MapPath(ImageProcessorConfiguration.Instance.VirtualCachePath);
/// <summary> /// <summary>
/// The request path for the image. /// The request path for the image.
@ -72,15 +72,22 @@ namespace ImageProcessor.Web.Caching
/// The image name /// The image name
/// </summary> /// </summary>
private readonly string imageName; private readonly string imageName;
/// <summary>
/// The physical cached path.
/// </summary>
private string physicalCachedPath;
/// <summary>
/// The virtual cached path.
/// </summary>
private string virtualCachedPath;
#endregion #endregion
#region Constructors #region Constructors
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DiskCache"/> class. /// Initializes a new instance of the <see cref="DiskCache"/> class.
/// </summary> /// </summary>
/// <param name="request">
/// The request for the image.
/// </param>
/// <param name="requestPath"> /// <param name="requestPath">
/// The request path for the image. /// The request path for the image.
/// </param> /// </param>
@ -90,53 +97,41 @@ namespace ImageProcessor.Web.Caching
/// <param name="imageName"> /// <param name="imageName">
/// The image name. /// The image name.
/// </param> /// </param>
public DiskCache(HttpRequest request, string requestPath, string fullPath, string imageName) public DiskCache(string requestPath, string fullPath, string imageName)
{ {
this.request = request;
this.requestPath = requestPath; this.requestPath = requestPath;
this.fullPath = fullPath; this.fullPath = fullPath;
this.imageName = imageName; this.imageName = imageName;
// Get the physical and virtual paths.
this.GetCachePaths();
} }
#endregion #endregion
#region Methods
#region Internal
/// <summary> /// <summary>
/// Gets the full transformed cached path for the image. /// Gets the cached 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.
/// This allows us to store millions of images.
/// </summary> /// </summary>
/// <returns>The full cached path for the image.</returns> public string CachedPath
internal Task<string> GetCachePathAsync()
{ {
return TaskHelpers.Run<string>(this.GetCachePath); get
{
return this.physicalCachedPath;
}
} }
/// <summary> /// <summary>
/// Gets the virtual path to the cached processed image. /// Gets the cached path.
/// </summary> /// </summary>
/// <param name="cachedPath"> public string VirtualCachedPath
/// The path to the cached image.
/// </param>
/// <returns>
/// The virtual path to the cached processed image.
/// </returns>
internal string GetVirtualCachedPath(string cachedPath)
{ {
string applicationPath = this.request.PhysicalApplicationPath; get
string virtualDir = this.request.ApplicationPath;
virtualDir = virtualDir == "/" ? virtualDir : (virtualDir + "/");
if (applicationPath != null)
{ {
return cachedPath.Replace(applicationPath, virtualDir).Replace(@"\", "/"); return this.virtualCachedPath;
} }
throw new InvalidOperationException(
"We can only map an absolute back to a relative path if the application path is available.");
} }
#region Methods
#region Internal
/// <summary> /// <summary>
/// Adds an image to the cache. /// Adds an image to the cache.
/// </summary> /// </summary>
@ -256,15 +251,13 @@ namespace ImageProcessor.Web.Caching
} }
/// <summary> /// <summary>
/// Gets the full transformed cached path for the image. /// Gets the full transformed cached paths 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 millions of images. /// This allows us to store millions of images.
/// </summary> /// </summary>
/// <returns>The full cached path for the image.</returns> private void GetCachePaths()
private string GetCachePath()
{ {
string cachedPath = string.Empty;
string streamHash = string.Empty; string streamHash = string.Empty;
if (AbsoluteCachePath != null) if (AbsoluteCachePath != null)
@ -302,16 +295,16 @@ namespace ImageProcessor.Web.Caching
// Collision rate of about 1 in 10000 for the folder structure. // Collision rate of about 1 in 10000 for the folder structure.
string pathFromKey = string.Join("\\", encryptedName.ToCharArray().Take(6)); string pathFromKey = string.Join("\\", encryptedName.ToCharArray().Take(6));
string virtualPathFromKey = pathFromKey.Replace(@"\", "/");
string cachedFileName = string.Format( string cachedFileName = string.Format(
"{0}.{1}", "{0}.{1}",
encryptedName, encryptedName,
!string.IsNullOrWhiteSpace(parsedExtension) ? parsedExtension.Replace(".", string.Empty) : fallbackExtension); !string.IsNullOrWhiteSpace(parsedExtension) ? parsedExtension.Replace(".", string.Empty) : fallbackExtension);
cachedPath = Path.Combine(AbsoluteCachePath, pathFromKey, cachedFileName); this.physicalCachedPath = Path.Combine(AbsoluteCachePath, pathFromKey, cachedFileName);
this.virtualCachedPath = Path.Combine(VirtualCachePath, virtualPathFromKey, cachedFileName).Replace(@"\", "/");
} }
return cachedPath;
} }
/// <summary> /// <summary>

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

@ -13,7 +13,6 @@ namespace ImageProcessor.Web.HttpModules
#region Using #region Using
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -157,12 +156,11 @@ namespace ImageProcessor.Web.HttpModules
/// The id representing the <see cref="T:System.Threading.SemaphoreSlim"/>. /// The id representing the <see cref="T:System.Threading.SemaphoreSlim"/>.
/// </param> /// </param>
/// <returns> /// <returns>
/// The <see cref="T:System.Threading.Mutex"/> for the given id. /// The <see cref="T:System.Threading.SemaphoreSlim"/> for the given id.
/// </returns> /// </returns>
private static SemaphoreSlim GetSemaphoreSlim(string id) private static SemaphoreSlim GetSemaphoreSlim(string id)
{ {
SemaphoreSlim semaphore = SemaphoreSlims.GetOrAdd(id, new SemaphoreSlim(1, 1)); return SemaphoreSlims.GetOrAdd(id, new SemaphoreSlim(1, 1));
return semaphore;
} }
/// <summary> /// <summary>
@ -179,12 +177,6 @@ namespace ImageProcessor.Web.HttpModules
if (disposing) if (disposing)
{ {
// Dispose of any managed resources here. // Dispose of any managed resources here.
foreach (KeyValuePair<string, SemaphoreSlim> semaphore in SemaphoreSlims)
{
semaphore.Value.Dispose();
}
SemaphoreSlims.Clear();
} }
// Call the appropriate methods to clean up // Call the appropriate methods to clean up
@ -346,15 +338,15 @@ namespace ImageProcessor.Web.HttpModules
} }
// Create a new cache to help process and cache the request. // Create a new cache to help process and cache the request.
DiskCache cache = new DiskCache(request, requestPath, fullPath, imageName); DiskCache cache = new DiskCache(requestPath, fullPath, imageName);
string cachedPath = await cache.GetCachePathAsync(); string cachedPath = cache.CachedPath;
// Since we are now rewriting the path we need to check again that the current user has access // Since we are now rewriting the path we need to check again that the current user has access
// to the rewritten path. // to the rewritten path.
// Get the user for the current request // Get the user for the current request
// If the user is anonymous or authentication doesn't work for this suffix avoid a NullReferenceException // If the user is anonymous or authentication doesn't work for this suffix avoid a NullReferenceException
// in the UrlAuthorizationModule by creating a generic identity. // in the UrlAuthorizationModule by creating a generic identity.
string virtualCachedPath = cache.GetVirtualCachedPath(cachedPath); string virtualCachedPath = cache.VirtualCachedPath;
IPrincipal user = context.User ?? new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]); IPrincipal user = context.User ?? new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]);
@ -362,7 +354,7 @@ namespace ImageProcessor.Web.HttpModules
PermissionSet permission = new PermissionSet(PermissionState.None); PermissionSet permission = new PermissionSet(PermissionState.None);
permission.AddPermission(new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted)); permission.AddPermission(new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted));
bool hasPermission = permission.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet); bool hasPermission = permission.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet);
bool isAllowed = true; bool isAllowed = true;
// Run the rewritten path past the authorization system again. // Run the rewritten path past the authorization system again.
@ -385,17 +377,19 @@ namespace ImageProcessor.Web.HttpModules
{ {
if (isRemote) if (isRemote)
{ {
Uri uri = new Uri(requestPath + "?" + urlParameters);
RemoteFile remoteFile = new RemoteFile(uri, false);
// Prevent response blocking.
WebResponse webResponse = await remoteFile.GetWebResponseAsync().ConfigureAwait(false);
SemaphoreSlim semaphore = GetSemaphoreSlim(cachedPath); SemaphoreSlim semaphore = GetSemaphoreSlim(cachedPath);
#if NET45 && !__MonoCS__
await semaphore.WaitAsync();
#else
semaphore.Wait();
#endif
try try
{ {
semaphore.Wait(); Uri uri = new Uri(requestPath + "?" + urlParameters);
RemoteFile remoteFile = new RemoteFile(uri, false);
// Prevent response blocking.
WebResponse webResponse = await remoteFile.GetWebResponseAsync().ConfigureAwait(false);
using (MemoryStream memoryStream = new MemoryStream()) using (MemoryStream memoryStream = new MemoryStream())
{ {
@ -435,19 +429,22 @@ namespace ImageProcessor.Web.HttpModules
} }
else else
{ {
// Check to see if the file exists.
// ReSharper disable once AssignNullToNotNullAttribute
FileInfo fileInfo = new FileInfo(requestPath);
if (!fileInfo.Exists)
{
throw new HttpException(404, "No image exists at " + fullPath);
}
SemaphoreSlim semaphore = GetSemaphoreSlim(cachedPath); SemaphoreSlim semaphore = GetSemaphoreSlim(cachedPath);
#if NET45 && !__MonoCS__
await semaphore.WaitAsync();
#else
semaphore.Wait();
#endif
try try
{ {
semaphore.Wait(); // Check to see if the file exists.
// ReSharper disable once AssignNullToNotNullAttribute
FileInfo fileInfo = new FileInfo(requestPath);
if (!fileInfo.Exists)
{
throw new HttpException(404, "No image exists at " + fullPath);
}
// Process the Image // Process the Image
imageFactory.Load(requestPath) imageFactory.Load(requestPath)

2
src/ImageProcessor/Imaging/Formats/GifFormat.cs

@ -86,7 +86,7 @@ namespace ImageProcessor.Imaging.Formats
foreach (GifFrame frame in info.GifFrames) foreach (GifFrame frame in info.GifFrames)
{ {
factory.Image = frame.Image; factory.Image = frame.Image;
frame.Image = quantizer.Quantize(processor.Invoke(factory)); frame.Image = quantizer.Quantize(processor.Invoke(factory));
encoder.AddFrame(frame); encoder.AddFrame(frame);
} }
} }

8
src/TestWebsites/NET45/Test_Website_NET45/Web.config

@ -6,17 +6,17 @@
<configuration> <configuration>
<configSections> <configSections>
<!--<sectionGroup name="imageProcessor"> <sectionGroup name="imageProcessor">
<section name="security" requirePermission="false" type="ImageProcessor.Web.Configuration.ImageSecuritySection, ImageProcessor.Web"/> <section name="security" requirePermission="false" type="ImageProcessor.Web.Configuration.ImageSecuritySection, ImageProcessor.Web"/>
<section name="processing" requirePermission="false" type="ImageProcessor.Web.Configuration.ImageProcessingSection, ImageProcessor.Web"/> <section name="processing" requirePermission="false" type="ImageProcessor.Web.Configuration.ImageProcessingSection, ImageProcessor.Web"/>
<section name="cache" requirePermission="false" type="ImageProcessor.Web.Configuration.ImageCacheSection, ImageProcessor.Web"/> <section name="cache" requirePermission="false" type="ImageProcessor.Web.Configuration.ImageCacheSection, ImageProcessor.Web"/>
</sectionGroup>--> </sectionGroup>
</configSections> </configSections>
<!--<imageProcessor > <imageProcessor >
<security configSource="config\imageprocessor\security.config"/> <security configSource="config\imageprocessor\security.config"/>
<cache configSource="config\imageprocessor\cache.config"/> <cache configSource="config\imageprocessor\cache.config"/>
<processing configSource="config\imageprocessor\processing.config"/> <processing configSource="config\imageprocessor\processing.config"/>
</imageProcessor>--> </imageProcessor>
<appSettings> <appSettings>
<add key="webpages:Version" value="2.0.0.0" /> <add key="webpages:Version" value="2.0.0.0" />
<add key="webpages:Enabled" value="false" /> <add key="webpages:Enabled" value="false" />

Loading…
Cancel
Save