Browse Source

Fixing redirect and cleanup.

Former-commit-id: 32e418b0f8b0b3b5a48dde36cc2f0ca52b8d3713
Former-commit-id: 31ab4e4c9d803625819d2646641c204460f69ed5
pull/17/head
James South 11 years ago
parent
commit
a49514bdd4
  1. 6
      build/build.xml
  2. 12
      src/ImageProcessor.Web.AzureBlobCache/AzureBlobCache.cs
  3. 4
      src/ImageProcessor.Web.PostProcessor/Properties/AssemblyInfo.cs
  4. 8
      src/ImageProcessor.Web.PostProcessor/README.md
  5. 1
      src/ImageProcessor.Web/Configuration/Resources/security.config
  6. 24
      src/ImageProcessor.Web/Helpers/ImageHelpers.cs
  7. 11
      src/ImageProcessor.Web/Helpers/RemoteFile.cs
  8. 82
      src/ImageProcessor.Web/HttpModules/ImageProcessingModule.cs
  9. 5
      src/ImageProcessor.Web/Services/RemoteImageService.cs
  10. 1
      src/ImageProcessor.sln.DotSettings
  11. 6
      src/ImageProcessor/ImageFactory.cs
  12. 1
      src/ImageProcessor/Imaging/Formats/FormatBase.cs
  13. 18
      src/TestWebsites/MVC/Global.asax.cs
  14. 3
      src/TestWebsites/MVC/Views/Home/Index.cshtml
  15. 2
      src/TestWebsites/MVC/config/imageprocessor/security.config

6
build/build.xml

@ -13,7 +13,7 @@
<project> <project>
<name>ImageProcessor Web</name> <name>ImageProcessor Web</name>
<version>4.1.5.0</version> <version>4.2.0.0</version>
<folder>..\src\ImageProcessor.Web</folder> <folder>..\src\ImageProcessor.Web</folder>
<projfile>ImageProcessor.Web.csproj</projfile> <projfile>ImageProcessor.Web.csproj</projfile>
<outputs> <outputs>
@ -24,7 +24,7 @@
<project> <project>
<name>ImageProcessor Web PostProcessor</name> <name>ImageProcessor Web PostProcessor</name>
<version>1.0.1.0</version> <version>1.0.2.0</version>
<folder>..\src\ImageProcessor.Web.PostProcessor</folder> <folder>..\src\ImageProcessor.Web.PostProcessor</folder>
<projfile>ImageProcessor.Web.PostProcessor.csproj</projfile> <projfile>ImageProcessor.Web.PostProcessor.csproj</projfile>
<outputs> <outputs>
@ -35,7 +35,7 @@
<project> <project>
<name>ImageProcessor Web.config sample</name> <name>ImageProcessor Web.config sample</name>
<version>2.1.1.0</version> <version>2.2.0.0</version>
<nuspec>ImageProcessor.Web.Config.nuspec</nuspec> <nuspec>ImageProcessor.Web.Config.nuspec</nuspec>
</project> </project>

12
src/ImageProcessor.Web.AzureBlobCache/AzureBlobCache.cs

@ -17,6 +17,7 @@ namespace ImageProcessor.Web.Caching
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web; using System.Web;
@ -217,16 +218,16 @@ namespace ImageProcessor.Web.Caching
Uri uri = new Uri(this.CachedPath); Uri uri = new Uri(this.CachedPath);
string path = uri.GetLeftPart(UriPartial.Path).Substring(this.cloudCachedBlobContainer.Uri.ToString().Length + 1); string path = uri.GetLeftPart(UriPartial.Path).Substring(this.cloudCachedBlobContainer.Uri.ToString().Length + 1);
string directory = path.Substring(0, path.LastIndexOf('/')); string directory = path.Substring(0, path.LastIndexOf('/'));
string parent = directory.Substring(path.LastIndexOf('/')); string parent = directory.Substring(0, directory.LastIndexOf('/'));
BlobContinuationToken continuationToken = null; BlobContinuationToken continuationToken = null;
CloudBlobDirectory directoryBlob = this.cloudCachedBlobContainer.GetDirectoryReference(parent);
List<IListBlobItem> results = new List<IListBlobItem>(); List<IListBlobItem> results = new List<IListBlobItem>();
// Loop through the all the files in a non blocking fashion. // Loop through the all the files in a non blocking fashion.
do do
{ {
BlobResultSegment response = await directoryBlob.ListBlobsSegmentedAsync(continuationToken); BlobResultSegment response = await this.cloudCachedBlobContainer
.ListBlobsSegmentedAsync(parent, true, BlobListingDetails.Metadata, 5000, continuationToken, null, null);
continuationToken = response.ContinuationToken; continuationToken = response.ContinuationToken;
results.AddRange(response.Results); results.AddRange(response.Results);
} }
@ -280,7 +281,10 @@ namespace ImageProcessor.Web.Caching
} }
else else
{ {
string blobPath = this.CachedPath.Substring(this.cloudSourceBlobContainer.Uri.ToString().Length + 1); Regex regex = new Regex("^http(s)?://");
string container = regex.Replace(this.cloudSourceBlobContainer.Uri.ToString(), string.Empty);
string blobPath = regex.Replace(this.RequestPath, string.Empty);
blobPath = blobPath.Replace(container, string.Empty).TrimStart('/');
CloudBlockBlob blockBlob = this.cloudSourceBlobContainer.GetBlockBlobReference(blobPath); CloudBlockBlob blockBlob = this.cloudSourceBlobContainer.GetBlockBlobReference(blobPath);
if (await blockBlob.ExistsAsync()) if (await blockBlob.ExistsAsync())

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

@ -32,5 +32,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("1.0.1.0")] // [assembly: AssemblyVersion("1.0.1.0")]
[assembly: AssemblyVersion("1.0.1.0")] [assembly: AssemblyVersion("1.0.2.0")]
[assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyFileVersion("1.0.2.0")]

8
src/ImageProcessor.Web.PostProcessor/README.md

@ -1,13 +1,13 @@
#Resource locations #Resource locations
###gifsicle ###gifsicle
http://www.lcdf.org/gifsicle/ [http://www.lcdf.org/gifsicle/](http://www.lcdf.org/gifsicle/)
###jpegtran ###jpegtran
http://jpegclub.org/jpegtran/ [http://jpegclub.org/jpegtran/](http://jpegclub.org/jpegtran/)
###optipng ###optipng
http://optipng.sourceforge.net/ [http://optipng.sourceforge.net/](http://optipng.sourceforge.net/)
###pngout ###pngout
http://advsys.net/ken/utils.htm [http://advsys.net/ken/utils.htm](http://advsys.net/ken/utils.htm)

1
src/ImageProcessor.Web/Configuration/Resources/security.config

@ -5,6 +5,7 @@
<settings> <settings>
<setting key="MaxBytes" value="4194304"/> <setting key="MaxBytes" value="4194304"/>
<setting key="Timeout" value="3000"/> <setting key="Timeout" value="3000"/>
<setting key="Protocol" value="http"/>
</settings> </settings>
<whitelist> <whitelist>
</whitelist> </whitelist>

24
src/ImageProcessor.Web/Helpers/ImageHelpers.cs

@ -104,30 +104,6 @@ namespace ImageProcessor.Web.Helpers
return string.Empty; return string.Empty;
} }
/// <summary>
/// Get the correct mime-type for the given string input.
/// </summary>
/// <param name="path">
/// The path to the cached image.
/// </param>
/// <returns>
/// The <see cref="string"/> matching the correct mime-type.
/// </returns>
public static string GetMimeType(string path)
{
using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false))
{
ISupportedImageFormat format = FormatUtilities.GetFormat(file);
if (format != null)
{
return format.MimeType;
}
}
return string.Empty;
}
/// <summary> /// <summary>
/// Builds a regular expression from the <see cref="T:ImageProcessor.Imaging.Formats.ISupportedImageFormat"/> type, this allows extensibility. /// Builds a regular expression from the <see cref="T:ImageProcessor.Imaging.Formats.ISupportedImageFormat"/> type, this allows extensibility.
/// </summary> /// </summary>

11
src/ImageProcessor.Web/Helpers/RemoteFile.cs

@ -173,16 +173,20 @@ namespace ImageProcessor.Web.Helpers
/// </returns> /// </returns>
internal async Task<WebResponse> GetWebResponseAsync() internal async Task<WebResponse> GetWebResponseAsync()
{ {
WebResponse response; WebResponse response = null;
try try
{ {
response = await this.GetWebRequest().GetResponseAsync(); response = await this.GetWebRequest().GetResponseAsync();
} }
catch (WebException ex) catch (WebException ex)
{ {
if (ex.Status == WebExceptionStatus.NameResolutionFailure) if (response != null)
{ {
throw new HttpException(404, "No image exists at " + Uri); HttpWebResponse errorResponse = (HttpWebResponse)ex.Response;
if (errorResponse.StatusCode == HttpStatusCode.NotFound)
{
throw new HttpException(404, "No image exists at " + this.Uri);
}
} }
throw; throw;
@ -217,7 +221,6 @@ namespace ImageProcessor.Web.Helpers
#endregion #endregion
#region Private #region Private
/// <summary> /// <summary>
/// Creates the WebRequest object used internally for this RemoteFile instance. /// Creates the WebRequest object used internally for this RemoteFile instance.
/// </summary> /// </summary>

82
src/ImageProcessor.Web/HttpModules/ImageProcessingModule.cs

@ -148,7 +148,7 @@ namespace ImageProcessor.Web.HttpModules
context.AddOnPostAuthorizeRequestAsync(postAuthorizeHelper.BeginEventHandler, postAuthorizeHelper.EndEventHandler); context.AddOnPostAuthorizeRequestAsync(postAuthorizeHelper.BeginEventHandler, postAuthorizeHelper.EndEventHandler);
EventHandlerTaskAsyncHelper postProcessHelper = new EventHandlerTaskAsyncHelper(this.PostProcessImage); EventHandlerTaskAsyncHelper postProcessHelper = new EventHandlerTaskAsyncHelper(this.PostProcessImage);
context.AddOnPostRequestHandlerExecuteAsync(postProcessHelper.BeginEventHandler, postProcessHelper.EndEventHandler); context.AddOnEndRequestAsync(postProcessHelper.BeginEventHandler, postProcessHelper.EndEventHandler);
context.PreSendRequestHeaders += this.ContextPreSendRequestHeaders; context.PreSendRequestHeaders += this.ContextPreSendRequestHeaders;
} }
@ -231,11 +231,11 @@ namespace ImageProcessor.Web.HttpModules
if (cachedPathObject != null) if (cachedPathObject != null)
{ {
string cachedPath = cachedPathObject.ToString();
// Trim the cache. // Trim the cache.
await this.imageCache.TrimCacheAsync(); await this.imageCache.TrimCacheAsync();
string cachedPath = cachedPathObject.ToString();
// Fire the post processing event. // Fire the post processing event.
EventHandler<PostProcessingEventArgs> handler = OnPostProcessing; EventHandler<PostProcessingEventArgs> handler = OnPostProcessing;
if (handler != null) if (handler != null)
@ -300,6 +300,7 @@ namespace ImageProcessor.Web.HttpModules
string queryString = string.Empty; string queryString = string.Empty;
string urlParameters = string.Empty; string urlParameters = string.Empty;
// Legacy support. I'd like to remove this asap.
if (hasMultiParams) if (hasMultiParams)
{ {
// We need to split the querystring to get the actual values we want. // We need to split the querystring to get the actual values we want.
@ -338,7 +339,13 @@ namespace ImageProcessor.Web.HttpModules
} }
else else
{ {
requestPath = HttpUtility.UrlDecode(request.QueryString.ToString()); // Parse any protocol values from settings.
string protocol = currentService.Settings["Protocol"] != null
? currentService.Settings["Protocol"] + "://"
: string.Empty;
requestPath = protocol + request.Path.Replace(currentService.Prefix, string.Empty).TrimStart('/');
queryString = HttpUtility.UrlDecode(request.QueryString.ToString());
} }
} }
@ -359,6 +366,7 @@ namespace ImageProcessor.Web.HttpModules
string fullPath = string.Format("{0}{1}?{2}", requestPath, parts, queryString); string fullPath = string.Format("{0}{1}?{2}", requestPath, parts, queryString);
object resourcePath; object resourcePath;
// More legacy support code.
if (hasMultiParams) if (hasMultiParams)
{ {
resourcePath = string.IsNullOrWhiteSpace(urlParameters) resourcePath = string.IsNullOrWhiteSpace(urlParameters)
@ -394,50 +402,55 @@ namespace ImageProcessor.Web.HttpModules
{ {
byte[] imageBuffer = await currentService.GetImage(resourcePath); byte[] imageBuffer = await currentService.GetImage(resourcePath);
using (MemoryStream memoryStream = new MemoryStream(imageBuffer)) using (MemoryStream inStream = new MemoryStream(imageBuffer))
{ {
// Reset the position of the stream to ensure we're reading the correct part.
memoryStream.Position = 0;
// Process the Image // Process the Image
imageFactory.Load(memoryStream).AutoProcess(queryString).Save(memoryStream); using (MemoryStream outStream = new MemoryStream())
memoryStream.Position = 0; {
imageFactory.Load(inStream).AutoProcess(queryString).Save(outStream);
// Add to the cache. // Add to the cache.
await this.imageCache.AddImageToCacheAsync(memoryStream, imageFactory.CurrentImageFormat.MimeType); await this.imageCache.AddImageToCacheAsync(outStream, imageFactory.CurrentImageFormat.MimeType);
}
}
// Store the cached path, response type, and cache dependency in the context for later retrieval. // Store the cached path, response type, and cache dependency in the context for later retrieval.
context.Items[CachedPathKey] = cachedPath; context.Items[CachedPathKey] = cachedPath;
context.Items[CachedResponseTypeKey] = imageFactory.CurrentImageFormat.MimeType; context.Items[CachedResponseTypeKey] = imageFactory.CurrentImageFormat.MimeType;
bool isFileCached = new Uri(cachedPath).IsFile; bool isFileCached = new Uri(cachedPath).IsFile;
if (isFileLocal) if (isFileLocal)
{
if (isFileCached)
{ {
if (isFileCached) // Some services might only provide filename so we can't monitor for the browser.
{ context.Items[CachedResponseFileDependency] = Path.GetFileName(requestPath) == requestPath
// Some services might only provide filename so we can't monitor for the browser. ? new List<string> { cachedPath }
context.Items[CachedResponseFileDependency] = Path.GetFileName(requestPath) == requestPath : new List<string> { requestPath, cachedPath };
? new List<string> { cachedPath }
: new List<string> { requestPath, cachedPath };
}
else
{
context.Items[CachedResponseFileDependency] = Path.GetFileName(requestPath) == requestPath
? null
: new List<string> { requestPath };
}
} }
else if (isFileCached) else
{ {
context.Items[CachedResponseFileDependency] = new List<string> { cachedPath }; context.Items[CachedResponseFileDependency] = Path.GetFileName(requestPath) == requestPath
? null
: new List<string> { requestPath };
} }
} }
else if (isFileCached)
{
context.Items[CachedResponseFileDependency] = new List<string> { cachedPath };
}
} }
} }
} }
// The cached file is valid so just rewrite the path. // The cached file is valid so just rewrite the path.
this.imageCache.RewritePath(context); this.imageCache.RewritePath(context);
// Redirect if not a locally store file.
if (!new Uri(cachedPath).IsFile)
{
context.ApplicationInstance.CompleteRequest();
}
} }
} }
@ -557,11 +570,11 @@ namespace ImageProcessor.Web.HttpModules
IImageService imageService = null; IImageService imageService = null;
IList<IImageService> services = ImageProcessorConfiguration.Instance.ImageServices; IList<IImageService> services = ImageProcessorConfiguration.Instance.ImageServices;
string path = request.Path; string path = request.Path.TrimStart('/');
foreach (IImageService service in services) foreach (IImageService service in services)
{ {
string key = service.Prefix; string key = service.Prefix;
if (!string.IsNullOrWhiteSpace(key) && path.EndsWith(key, StringComparison.InvariantCultureIgnoreCase)) if (!string.IsNullOrWhiteSpace(key) && path.StartsWith(key, StringComparison.InvariantCultureIgnoreCase))
{ {
imageService = service; imageService = service;
} }
@ -575,7 +588,6 @@ namespace ImageProcessor.Web.HttpModules
// Return the file based service // Return the file based service
return services.FirstOrDefault(s => string.IsNullOrWhiteSpace(s.Prefix) && s.IsValidRequest(path)); return services.FirstOrDefault(s => string.IsNullOrWhiteSpace(s.Prefix) && s.IsValidRequest(path));
} }
#endregion #endregion
} }
} }

5
src/ImageProcessor.Web/Services/RemoteImageService.cs

@ -37,7 +37,8 @@ namespace ImageProcessor.Web.Services
this.Settings = new Dictionary<string, string> this.Settings = new Dictionary<string, string>
{ {
{ "MaxBytes", "4194304" }, { "MaxBytes", "4194304" },
{ "Timeout", "30000" } { "Timeout", "30000" },
{ "Protocol", "http" }
}; };
this.WhiteList = new Uri[] { }; this.WhiteList = new Uri[] { };
@ -105,7 +106,7 @@ namespace ImageProcessor.Web.Services
{ {
if (!uri.IsAbsoluteUri) if (!uri.IsAbsoluteUri)
{ {
Uri rebaseUri = new Uri("http://" + uri.ToString().TrimStart(new[] { '.', '/' })); Uri rebaseUri = new Uri("http://" + uri.ToString().TrimStart('.', '/'));
validUrl = upper.StartsWith(rebaseUri.Host.ToUpperInvariant()) || upper.EndsWith(rebaseUri.Host.ToUpperInvariant()); validUrl = upper.StartsWith(rebaseUri.Host.ToUpperInvariant()) || upper.EndsWith(rebaseUri.Host.ToUpperInvariant());
} }
else else

1
src/ImageProcessor.sln.DotSettings

@ -18,5 +18,6 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=REF/@EntryIndexedValue">REF</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=REF/@EntryIndexedValue">REF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RGB/@EntryIndexedValue">RGB</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RGB/@EntryIndexedValue">RGB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RGBA/@EntryIndexedValue">RGBA</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RGBA/@EntryIndexedValue">RGBA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SHA/@EntryIndexedValue">SHA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SRGB/@EntryIndexedValue">SRGB</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SRGB/@EntryIndexedValue">SRGB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SS/@EntryIndexedValue">SS</s:String></wpf:ResourceDictionary> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SS/@EntryIndexedValue">SS</s:String></wpf:ResourceDictionary>

6
src/ImageProcessor/ImageFactory.cs

@ -139,6 +139,9 @@ namespace ImageProcessor
/// </returns> /// </returns>
public ImageFactory Load(Stream stream) public ImageFactory Load(Stream stream)
{ {
// Reset the position of the stream to ensure we're reading the correct part.
stream.Position = 0;
ISupportedImageFormat format = FormatUtilities.GetFormat(stream); ISupportedImageFormat format = FormatUtilities.GetFormat(stream);
if (format == null) if (format == null)
@ -1070,8 +1073,9 @@ namespace ImageProcessor
if (this.ShouldProcess) if (this.ShouldProcess)
{ {
// Allow the same stream to be used as for input. // Allow the same stream to be used as for input.
stream.Position = 0; stream.SetLength(0);
this.Image = this.CurrentImageFormat.Save(stream, this.Image); this.Image = this.CurrentImageFormat.Save(stream, this.Image);
stream.Position = 0;
} }
return this; return this;

1
src/ImageProcessor/Imaging/Formats/FormatBase.cs

@ -108,7 +108,6 @@ namespace ImageProcessor.Imaging.Formats
public virtual Image Save(Stream stream, Image image) public virtual Image Save(Stream stream, Image image)
{ {
image.Save(stream, this.ImageFormat); image.Save(stream, this.ImageFormat);
stream.Position = 0;
return image; return image;
} }

18
src/TestWebsites/MVC/Global.asax.cs

@ -28,17 +28,17 @@ namespace Test_Website_NET45
RouteConfig.RegisterRoutes(RouteTable.Routes); RouteConfig.RegisterRoutes(RouteTable.Routes);
// Test the post processing event. // Test the post processing event.
ImageProcessingModule.OnPostProcessing += (sender, args) => Debug.WriteLine(args.CachedImagePath); //ImageProcessingModule.OnPostProcessing += (sender, args) => Debug.WriteLine(args.CachedImagePath);
ImageProcessingModule.OnProcessQuerystring += (sender, args) => //ImageProcessingModule.OnProcessQuerystring += (sender, args) =>
{ // {
if (!args.RawUrl.Contains("penguins")) // if (!args.RawUrl.Contains("penguins"))
{ // {
return args.Querystring += "watermark=protected&color=fff&fontsize=36&fontopacity=70textshadow=true&fontfamily=arial"; // return args.Querystring += "watermark=protected&color=fff&fontsize=36&fontopacity=70textshadow=true&fontfamily=arial";
} // }
return args.Querystring; // return args.Querystring;
}; // };
} }
private async void WritePath(object sender, PostProcessingEventArgs e) private async void WritePath(object sender, PostProcessingEventArgs e)

3
src/TestWebsites/MVC/Views/Home/Index.cshtml

@ -7,7 +7,8 @@
<div class="row"> <div class="row">
<div class="col-s-6"> <div class="col-s-6">
<h2>Resized</h2> <h2>Resized</h2>
<img src="/images/format-Penguins.jpg?width=310" /> <img src="/images/format.Penguins.jpg?width=302"/>
<img src="/remote.axd/ipcache.blob.core.windows.net/source/IMG_0671.JPG?width=302&filter=comic" />
@*<h3>Foreign language test.</h3> @*<h3>Foreign language test.</h3>
<img src="/images/udendørs.jpg?width=300" /> <img src="/images/udendørs.jpg?width=300" />
<img src="/images/udendørs.jpg?preset=demo&filter=comic" /> <img src="/images/udendørs.jpg?preset=demo&filter=comic" />

2
src/TestWebsites/MVC/config/imageprocessor/security.config

@ -6,8 +6,10 @@
<settings> <settings>
<setting key="MaxBytes" value="4194304"/> <setting key="MaxBytes" value="4194304"/>
<setting key="Timeout" value="30000"/> <setting key="Timeout" value="30000"/>
<setting key="Protocol" value="http"/>
</settings> </settings>
<whitelist> <whitelist>
<add url="http://ipcache.blob.core.windows.net/"/>
<add url="images.mymovies.net"/> <add url="images.mymovies.net"/>
<add url="http://maps.googleapis.com"/> <add url="http://maps.googleapis.com"/>
<add url="fbcdn"/> <add url="fbcdn"/>

Loading…
Cancel
Save