mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 282fdc30f4a813fe8fe759a21debfb6142676f52 Former-commit-id: 39a6b41dea2323461008da07120c387169913523pull/17/head
11 changed files with 689 additions and 103 deletions
@ -0,0 +1,98 @@ |
|||
namespace ImageProcessor.Web.AzureBlobCache |
|||
{ |
|||
using System; |
|||
using System.Configuration; |
|||
using System.Threading.Tasks; |
|||
using System.Web; |
|||
|
|||
using ImageProcessor.Web.Caching; |
|||
|
|||
using Microsoft.WindowsAzure; |
|||
using Microsoft.WindowsAzure.Storage; |
|||
using Microsoft.WindowsAzure.Storage.Blob; |
|||
|
|||
public class AzureBlobCache : ImageCacheBase |
|||
{ |
|||
private CloudStorageAccount cloudStorageAccount; |
|||
|
|||
private CloudBlobClient cloudBlobClient; |
|||
|
|||
private CloudBlobContainer cloudBlobContainer; |
|||
|
|||
public AzureBlobCache(string requestPath, string fullPath, string querystring) |
|||
: base(requestPath, fullPath, querystring) |
|||
{ |
|||
// TODO: These should all be in the configuration.
|
|||
|
|||
// Retrieve storage account from connection string.
|
|||
this.cloudStorageAccount = CloudStorageAccount.Parse( |
|||
CloudConfigurationManager.GetSetting("StorageConnectionString")); |
|||
|
|||
// Create the blob client.
|
|||
this.cloudBlobClient = this.cloudStorageAccount.CreateCloudBlobClient(); |
|||
|
|||
// Retrieve reference to a previously created container.
|
|||
this.cloudBlobContainer = this.cloudBlobClient.GetContainerReference("mycontainer"); |
|||
} |
|||
|
|||
public override int MaxAge |
|||
{ |
|||
get { throw new System.NotImplementedException(); } |
|||
} |
|||
|
|||
public override async Task<bool> IsNewOrUpdatedAsync() |
|||
{ |
|||
string cachedFileName = await this.CreateCachedFileName(); |
|||
|
|||
// TODO: Generate cache path.
|
|||
CloudBlockBlob blockBlob = new CloudBlockBlob(new Uri("")); |
|||
|
|||
bool isUpdated = false; |
|||
if (!await blockBlob.ExistsAsync()) |
|||
{ |
|||
// Nothing in the cache so we should return true.
|
|||
isUpdated = true; |
|||
} |
|||
else if (blockBlob.Properties.LastModified.HasValue) |
|||
{ |
|||
// Check to see if the cached image is set to expire.
|
|||
if (this.IsExpired(blockBlob.Properties.LastModified.Value.UtcDateTime)) |
|||
{ |
|||
isUpdated = true; |
|||
} |
|||
} |
|||
|
|||
return isUpdated; |
|||
} |
|||
|
|||
public override async Task AddImageToCacheAsync(System.IO.Stream stream) |
|||
{ |
|||
throw new System.NotImplementedException(); |
|||
} |
|||
|
|||
public override async Task TrimCacheAsync() |
|||
{ |
|||
throw new System.NotImplementedException(); |
|||
} |
|||
|
|||
public override void RewritePath(HttpContext context) |
|||
{ |
|||
throw new System.NotImplementedException(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the given images creation date is out with
|
|||
/// the prescribed limit.
|
|||
/// </summary>
|
|||
/// <param name="creationDate">
|
|||
/// The creation date.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The true if the date is out with the limit, otherwise; false.
|
|||
/// </returns>
|
|||
private bool IsExpired(DateTime creationDate) |
|||
{ |
|||
return creationDate.AddDays(this.MaxAge) < DateTime.UtcNow.AddDays(-this.MaxAge); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|||
<PropertyGroup> |
|||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
|||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
|||
<ProjectGuid>{3C805E4C-D679-43F8-8C43-8909CDB4D4D7}</ProjectGuid> |
|||
<OutputType>Library</OutputType> |
|||
<AppDesignerFolder>Properties</AppDesignerFolder> |
|||
<RootNamespace>ImageProcessor.Web.AzureBlobCache</RootNamespace> |
|||
<AssemblyName>ImageProcessor.Web.AzureBlobCache</AssemblyName> |
|||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> |
|||
<FileAlignment>512</FileAlignment> |
|||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> |
|||
<RestorePackages>true</RestorePackages> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
|||
<DebugSymbols>true</DebugSymbols> |
|||
<DebugType>full</DebugType> |
|||
<Optimize>false</Optimize> |
|||
<OutputPath>bin\Debug\</OutputPath> |
|||
<DefineConstants>DEBUG;TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|||
<DebugType>pdbonly</DebugType> |
|||
<Optimize>true</Optimize> |
|||
<OutputPath>bin\Release\</OutputPath> |
|||
<DefineConstants>TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<Reference Include="Microsoft.Data.Edm"> |
|||
<HintPath>..\packages\Microsoft.Data.Edm.5.6.2\lib\net40\Microsoft.Data.Edm.dll</HintPath> |
|||
<Private>True</Private> |
|||
</Reference> |
|||
<Reference Include="Microsoft.Data.OData"> |
|||
<HintPath>..\packages\Microsoft.Data.OData.5.6.2\lib\net40\Microsoft.Data.OData.dll</HintPath> |
|||
<Private>True</Private> |
|||
</Reference> |
|||
<Reference Include="Microsoft.Data.Services.Client, Version=5.6.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> |
|||
<SpecificVersion>False</SpecificVersion> |
|||
<HintPath>..\packages\Microsoft.Data.Services.Client.5.6.2\lib\net40\Microsoft.Data.Services.Client.dll</HintPath> |
|||
</Reference> |
|||
<Reference Include="Microsoft.WindowsAzure.Configuration"> |
|||
<HintPath>..\packages\Microsoft.WindowsAzure.ConfigurationManager.1.8.0.0\lib\net35-full\Microsoft.WindowsAzure.Configuration.dll</HintPath> |
|||
</Reference> |
|||
<Reference Include="Newtonsoft.Json"> |
|||
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net45\Newtonsoft.Json.dll</HintPath> |
|||
<Private>True</Private> |
|||
</Reference> |
|||
<Reference Include="System" /> |
|||
<Reference Include="System.Core" /> |
|||
<Reference Include="System.Spatial, Version=5.6.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> |
|||
<SpecificVersion>False</SpecificVersion> |
|||
<HintPath>..\packages\System.Spatial.5.6.2\lib\net40\System.Spatial.dll</HintPath> |
|||
</Reference> |
|||
<Reference Include="System.Web" /> |
|||
<Reference Include="System.Xml.Linq" /> |
|||
<Reference Include="System.Data.DataSetExtensions" /> |
|||
<Reference Include="Microsoft.CSharp" /> |
|||
<Reference Include="System.Data" /> |
|||
<Reference Include="System.Xml" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Compile Include="AzureBlobCache.cs" /> |
|||
<Compile Include="Properties\AssemblyInfo.cs" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\ImageProcessor.Web\ImageProcessor.Web.csproj"> |
|||
<Project>{d011a778-59c8-4bfa-a770-c350216bf161}</Project> |
|||
<Name>ImageProcessor.Web</Name> |
|||
</ProjectReference> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<None Include="packages.config" /> |
|||
</ItemGroup> |
|||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
|||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" /> |
|||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> |
|||
<PropertyGroup> |
|||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> |
|||
</PropertyGroup> |
|||
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" /> |
|||
</Target> |
|||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
|||
Other similar extension points exist, see Microsoft.Common.targets. |
|||
<Target Name="BeforeBuild"> |
|||
</Target> |
|||
<Target Name="AfterBuild"> |
|||
</Target> |
|||
--> |
|||
</Project> |
|||
@ -0,0 +1,36 @@ |
|||
using System.Reflection; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
// General Information about an assembly is controlled through the following
|
|||
// set of attributes. Change these attribute values to modify the information
|
|||
// associated with an assembly.
|
|||
[assembly: AssemblyTitle("ImageProcessor.Web.AzureBlobCache")] |
|||
[assembly: AssemblyDescription("")] |
|||
[assembly: AssemblyConfiguration("")] |
|||
[assembly: AssemblyCompany("")] |
|||
[assembly: AssemblyProduct("ImageProcessor.Web.AzureBlobCache")] |
|||
[assembly: AssemblyCopyright("Copyright © 2015")] |
|||
[assembly: AssemblyTrademark("")] |
|||
[assembly: AssemblyCulture("")] |
|||
|
|||
// Setting ComVisible to false makes the types in this assembly not visible
|
|||
// to COM components. If you need to access a type in this assembly from
|
|||
// COM, set the ComVisible attribute to true on that type.
|
|||
[assembly: ComVisible(false)] |
|||
|
|||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|||
[assembly: Guid("90605e94-25f4-4c69-b602-6b1df0adb89e")] |
|||
|
|||
// Version information for an assembly consists of the following four values:
|
|||
//
|
|||
// Major Version
|
|||
// Minor Version
|
|||
// Build Number
|
|||
// Revision
|
|||
//
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
|||
// by using the '*' as shown below:
|
|||
// [assembly: AssemblyVersion("1.0.*")]
|
|||
[assembly: AssemblyVersion("1.0.0.0")] |
|||
[assembly: AssemblyFileVersion("1.0.0.0")] |
|||
@ -0,0 +1,10 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<packages> |
|||
<package id="Microsoft.Data.Edm" version="5.6.2" targetFramework="net45" /> |
|||
<package id="Microsoft.Data.OData" version="5.6.2" targetFramework="net45" /> |
|||
<package id="Microsoft.Data.Services.Client" version="5.6.2" targetFramework="net45" /> |
|||
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="1.8.0.0" targetFramework="net45" /> |
|||
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net45" /> |
|||
<package id="System.Spatial" version="5.6.2" targetFramework="net45" /> |
|||
<package id="WindowsAzure.Storage" version="4.3.0" targetFramework="net45" /> |
|||
</packages> |
|||
@ -0,0 +1,180 @@ |
|||
namespace ImageProcessor.Web.Caching |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using System.Web; |
|||
using System.Web.Hosting; |
|||
|
|||
using ImageProcessor.Web.Configuration; |
|||
using ImageProcessor.Web.Extensions; |
|||
|
|||
public class DiskCache2 : ImageCacheBase |
|||
{ |
|||
/// <summary>
|
|||
/// The maximum number of files allowed in the directory.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// NTFS directories can handle up to 10,000 files in the directory before slowing down.
|
|||
/// This will help us to ensure that don't go over that limit.
|
|||
/// <see href="http://stackoverflow.com/questions/197162/ntfs-performance-and-large-volumes-of-files-and-directories"/>
|
|||
/// <see href="http://stackoverflow.com/questions/115882/how-do-you-deal-with-lots-of-small-files"/>
|
|||
/// <see href="http://stackoverflow.com/questions/1638219/millions-of-small-graphics-files-and-how-to-overcome-slow-file-system-access-on"/>
|
|||
/// </remarks>
|
|||
private const int MaxFilesCount = 100; |
|||
|
|||
/// <summary>
|
|||
/// The virtual cache path.
|
|||
/// </summary>
|
|||
private static readonly string VirtualCachePath = ImageProcessorConfiguration.Instance.VirtualCachePath; |
|||
|
|||
/// <summary>
|
|||
/// The absolute path to virtual cache path on the server.
|
|||
/// TODO: Change this so configuration is determined per IImageCache instance.
|
|||
/// </summary>
|
|||
private static readonly string AbsoluteCachePath = HostingEnvironment.MapPath(VirtualCachePath); |
|||
|
|||
/// <summary>
|
|||
/// The physical cached path.
|
|||
/// </summary>
|
|||
private string physicalCachedPath; |
|||
|
|||
/// <summary>
|
|||
/// The virtual cached path.
|
|||
/// </summary>
|
|||
private string virtualCachedPath; |
|||
|
|||
public DiskCache2(string requestPath, string fullPath, string querystring) |
|||
: base(requestPath, fullPath, querystring) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The maximum number of days to cache files on the system for.
|
|||
/// TODO: Shift the getter source to proper config.
|
|||
/// </summary>
|
|||
public override int MaxAge |
|||
{ |
|||
get |
|||
{ |
|||
return ImageProcessorConfiguration.Instance.MaxCacheDays; |
|||
} |
|||
} |
|||
|
|||
public override async Task<bool> IsNewOrUpdatedAsync() |
|||
{ |
|||
string cachedFileName = await this.CreateCachedFileName(); |
|||
|
|||
// Collision rate of about 1 in 10000 for the folder structure.
|
|||
// That gives us massive scope for files.
|
|||
string pathFromKey = string.Join("\\", cachedFileName.ToCharArray().Take(6)); |
|||
string virtualPathFromKey = pathFromKey.Replace(@"\", "/"); |
|||
this.physicalCachedPath = Path.Combine(AbsoluteCachePath, pathFromKey, cachedFileName); |
|||
this.virtualCachedPath = Path.Combine(VirtualCachePath, virtualPathFromKey, cachedFileName).Replace(@"\", "/"); |
|||
this.CachedPath = this.physicalCachedPath; |
|||
|
|||
bool isUpdated = false; |
|||
CachedImage cachedImage = CacheIndexer.GetValue(this.physicalCachedPath); |
|||
|
|||
if (cachedImage == null) |
|||
{ |
|||
// Nothing in the cache so we should return true.
|
|||
isUpdated = true; |
|||
} |
|||
else |
|||
{ |
|||
// Check to see if the cached image is set to expire.
|
|||
if (this.IsExpired(cachedImage.CreationTimeUtc)) |
|||
{ |
|||
CacheIndexer.Remove(this.physicalCachedPath); |
|||
isUpdated = true; |
|||
} |
|||
} |
|||
|
|||
return isUpdated; |
|||
} |
|||
|
|||
public override async Task AddImageToCacheAsync(Stream stream) |
|||
{ |
|||
// ReSharper disable once AssignNullToNotNullAttribute
|
|||
DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetDirectoryName(this.physicalCachedPath)); |
|||
if (!directoryInfo.Exists) |
|||
{ |
|||
directoryInfo.Create(); |
|||
} |
|||
|
|||
using (FileStream fileStream = File.Create(this.physicalCachedPath)) |
|||
{ |
|||
await stream.CopyToAsync(fileStream); |
|||
} |
|||
} |
|||
|
|||
public override async Task TrimCacheAsync() |
|||
{ |
|||
string directory = Path.GetDirectoryName(this.physicalCachedPath); |
|||
|
|||
if (directory != null) |
|||
{ |
|||
DirectoryInfo directoryInfo = new DirectoryInfo(directory); |
|||
DirectoryInfo parentDirectoryInfo = directoryInfo.Parent; |
|||
|
|||
if (parentDirectoryInfo != null) |
|||
{ |
|||
// UNC folders can throw exceptions if the file doesn't exist.
|
|||
foreach (DirectoryInfo enumerateDirectory in await parentDirectoryInfo.SafeEnumerateDirectoriesAsync()) |
|||
{ |
|||
IEnumerable<FileInfo> files = enumerateDirectory.EnumerateFiles().OrderBy(f => f.CreationTimeUtc); |
|||
int count = files.Count(); |
|||
|
|||
foreach (FileInfo fileInfo in files) |
|||
{ |
|||
try |
|||
{ |
|||
// If the group count is equal to the max count minus 1 then we know we
|
|||
// have reduced the number of items below the maximum allowed.
|
|||
// We'll cleanup any orphaned expired files though.
|
|||
if (!this.IsExpired(fileInfo.CreationTimeUtc) && count <= MaxFilesCount - 1) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
// Remove from the cache and delete each CachedImage.
|
|||
CacheIndexer.Remove(fileInfo.Name); |
|||
fileInfo.Delete(); |
|||
count -= 1; |
|||
} |
|||
// ReSharper disable once EmptyGeneralCatchClause
|
|||
catch |
|||
{ |
|||
// Do nothing; skip to the next file.
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public override void RewritePath(HttpContext context) |
|||
{ |
|||
// The cached file is valid so just rewrite the path.
|
|||
context.RewritePath(this.virtualCachedPath, false); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the given images creation date is out with
|
|||
/// the prescribed limit.
|
|||
/// </summary>
|
|||
/// <param name="creationDate">
|
|||
/// The creation date.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The true if the date is out with the limit, otherwise; false.
|
|||
/// </returns>
|
|||
private bool IsExpired(DateTime creationDate) |
|||
{ |
|||
return creationDate.AddDays(this.MaxAge) < DateTime.UtcNow.AddDays(-this.MaxAge); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
|
|||
namespace ImageProcessor.Web.Caching |
|||
{ |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using System.Web; |
|||
|
|||
public interface IImageCache |
|||
{ |
|||
string CachedPath { get; } |
|||
|
|||
int MaxAge { get; } |
|||
|
|||
Task<bool> IsNewOrUpdatedAsync(); |
|||
|
|||
Task AddImageToCacheAsync(Stream stream); |
|||
|
|||
Task TrimCacheAsync(); |
|||
|
|||
Task<string> CreateCachedFileName(); |
|||
|
|||
void RewritePath(HttpContext context); |
|||
|
|||
//void SetHeaders(HttpContext context);
|
|||
} |
|||
} |
|||
@ -0,0 +1,125 @@ |
|||
namespace ImageProcessor.Web.Caching |
|||
{ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.IO; |
|||
using System.Reflection; |
|||
using System.Threading.Tasks; |
|||
using System.Web; |
|||
|
|||
using ImageProcessor.Web.Extensions; |
|||
using ImageProcessor.Web.Helpers; |
|||
|
|||
public abstract class ImageCacheBase : IImageCache |
|||
{ |
|||
/// <summary>
|
|||
/// The assembly version.
|
|||
/// </summary>
|
|||
private static readonly string AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); |
|||
|
|||
/// <summary>
|
|||
/// The request path for the image.
|
|||
/// </summary>
|
|||
private readonly string requestPath; |
|||
|
|||
/// <summary>
|
|||
/// The full path for the image.
|
|||
/// </summary>
|
|||
private readonly string fullPath; |
|||
|
|||
/// <summary>
|
|||
/// The querystring containing processing instructions.
|
|||
/// </summary>
|
|||
private readonly string querystring; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ImageCacheBase"/> class.
|
|||
/// </summary>
|
|||
/// <param name="requestPath">
|
|||
/// The request path for the image.
|
|||
/// </param>
|
|||
/// <param name="fullPath">
|
|||
/// The full path for the image.
|
|||
/// </param>
|
|||
/// <param name="querystring">
|
|||
/// The querystring containing instructions.
|
|||
/// </param>
|
|||
protected ImageCacheBase(string requestPath, string fullPath, string querystring) |
|||
{ |
|||
this.requestPath = requestPath; |
|||
this.fullPath = fullPath; |
|||
this.querystring = querystring; |
|||
} |
|||
|
|||
public string CachedPath { get; protected set; } |
|||
|
|||
public abstract int MaxAge { get; } |
|||
|
|||
public abstract Task<bool> IsNewOrUpdatedAsync(); |
|||
|
|||
public abstract Task AddImageToCacheAsync(Stream stream); |
|||
|
|||
public abstract Task TrimCacheAsync(); |
|||
|
|||
public Task<string> CreateCachedFileName() |
|||
{ |
|||
string streamHash = string.Empty; |
|||
|
|||
try |
|||
{ |
|||
if (new Uri(this.requestPath).IsFile) |
|||
{ |
|||
// Get the hash for the filestream. That way we can ensure that if the image is
|
|||
// updated but has the same name we will know.
|
|||
FileInfo imageFileInfo = new FileInfo(this.requestPath); |
|||
if (imageFileInfo.Exists) |
|||
{ |
|||
// Pull the latest info.
|
|||
imageFileInfo.Refresh(); |
|||
|
|||
// Checking the stream itself is far too processor intensive so we make a best guess.
|
|||
string creation = imageFileInfo.CreationTimeUtc.ToString(CultureInfo.InvariantCulture); |
|||
string length = imageFileInfo.Length.ToString(CultureInfo.InvariantCulture); |
|||
streamHash = string.Format("{0}{1}", creation, length); |
|||
} |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
streamHash = string.Empty; |
|||
} |
|||
|
|||
// Use an sha1 hash of the full path including the querystring to create the image name.
|
|||
// That name can also be used as a key for the cached image and we should be able to use
|
|||
// The characters of that hash as sub-folders.
|
|||
string parsedExtension = ImageHelpers.GetExtension(this.fullPath, this.querystring); |
|||
string encryptedName = (streamHash + this.fullPath).ToSHA1Fingerprint(); |
|||
|
|||
string cachedFileName = string.Format( |
|||
"{0}.{1}", |
|||
encryptedName, |
|||
!string.IsNullOrWhiteSpace(parsedExtension) ? parsedExtension.Replace(".", string.Empty) : "jpg"); |
|||
|
|||
this.CachedPath = cachedFileName; |
|||
return Task.FromResult(cachedFileName); |
|||
} |
|||
|
|||
public abstract void RewritePath(HttpContext context); |
|||
|
|||
public virtual void SetHeaders(HttpContext context, string responseType) |
|||
{ |
|||
HttpResponse response = context.Response; |
|||
|
|||
response.ContentType = responseType; |
|||
|
|||
if (response.Headers["Image-Served-By"] == null) |
|||
{ |
|||
response.AddHeader("Image-Served-By", "ImageProcessor.Web/" + AssemblyVersion); |
|||
} |
|||
|
|||
HttpCachePolicy cache = response.Cache; |
|||
cache.SetCacheability(HttpCacheability.Public); |
|||
cache.VaryByHeaders["Accept-Encoding"] = true; |
|||
} |
|||
} |
|||
} |
|||
@ -1,6 +1,7 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<repositories> |
|||
<repository path="..\ImageProcessor.UnitTests\packages.config" /> |
|||
<repository path="..\ImageProcessor.Web.AzureBlobCache\packages.config" /> |
|||
<repository path="..\ImageProcessor.Web.UnitTests\packages.config" /> |
|||
<repository path="..\TestWebsites\MVC\packages.config" /> |
|||
</repositories> |
|||
Loading…
Reference in new issue