diff --git a/src/ImageProcessor.Web/Configuration/ImageProcessingSection.cs b/src/ImageProcessor.Web/Configuration/ImageProcessingSection.cs
index 23f8bc9ca..6ece1681b 100644
--- a/src/ImageProcessor.Web/Configuration/ImageProcessingSection.cs
+++ b/src/ImageProcessor.Web/Configuration/ImageProcessingSection.cs
@@ -155,7 +155,7 @@ namespace ImageProcessor.Web.Configuration
///
/// The index at which to get the specified object.
///
- /// The the
+ /// The
/// at the specified index within the collection.
///
public PresetElement this[int index]
diff --git a/src/ImageProcessor.Web/Configuration/ImageProcessorConfiguration.cs b/src/ImageProcessor.Web/Configuration/ImageProcessorConfiguration.cs
index a9684c9c5..fde8deeb2 100644
--- a/src/ImageProcessor.Web/Configuration/ImageProcessorConfiguration.cs
+++ b/src/ImageProcessor.Web/Configuration/ImageProcessorConfiguration.cs
@@ -20,6 +20,7 @@ namespace ImageProcessor.Web.Configuration
using ImageProcessor.Common.Extensions;
using ImageProcessor.Processors;
using ImageProcessor.Web.Processors;
+ using ImageProcessor.Web.Services;
///
/// Encapsulates methods to allow the retrieval of ImageProcessor settings.
@@ -72,6 +73,7 @@ namespace ImageProcessor.Web.Configuration
private ImageProcessorConfiguration()
{
this.LoadGraphicsProcessors();
+ this.LoadImageServices();
}
#endregion
@@ -92,6 +94,11 @@ namespace ImageProcessor.Web.Configuration
///
public IList GraphicsProcessors { get; private set; }
+ ///
+ /// Gets the list of available ImageServices.
+ ///
+ public IList ImageServices { get; private set; }
+
///
/// Gets a value indicating whether to preserve exif meta data.
///
@@ -136,7 +143,7 @@ namespace ImageProcessor.Web.Configuration
{
get
{
- return GetImageSecuritySection().WhiteList.Cast().Select(x => x.Url).ToArray();
+ return GetImageSecuritySection().ImageServices.Cast().Select(x => x.Url).ToArray();
}
}
@@ -147,7 +154,7 @@ namespace ImageProcessor.Web.Configuration
{
get
{
- return GetImageSecuritySection().WhiteList.Cast().ToArray();
+ return GetImageSecuritySection().ImageServices.Cast().ToArray();
}
}
@@ -258,6 +265,43 @@ namespace ImageProcessor.Web.Configuration
});
}
+ ///
+ /// Returns the for the given plugin.
+ ///
+ ///
+ /// The name of the plugin to get the settings for.
+ ///
+ ///
+ /// The for the given plugin.
+ ///
+ public Dictionary GetServiceSettings(string name)
+ {
+ return PluginSettings.GetOrAdd(
+ name,
+ n =>
+ {
+ ImageSecuritySection.ServiceElement pluginElement = GetImageSecuritySection()
+ .ImageServices
+ .Cast()
+ .FirstOrDefault(x => x.Name == n);
+
+ Dictionary settings;
+
+ if (pluginElement != null)
+ {
+ settings = pluginElement.Settings
+ .Cast()
+ .ToDictionary(setting => setting.Key, setting => setting.Value);
+ }
+ else
+ {
+ settings = new Dictionary();
+ }
+
+ return settings;
+ });
+ }
+
///
/// Retrieves the processing configuration section from the current application configuration.
///
@@ -325,11 +369,51 @@ namespace ImageProcessor.Web.Configuration
}
}
+ ///
+ /// Gets the list of available ImageServices.
+ ///
+ private void LoadImageServices()
+ {
+ if (this.ImageServices == null)
+ {
+ if (GetImageSecuritySection().ImageServices.AutoLoadPlugins)
+ {
+ Type type = typeof(IImageService);
+ try
+ {
+ // Build a list of native IGraphicsProcessor instances.
+ List availableTypes = BuildManager.GetReferencedAssemblies()
+ .Cast()
+ .SelectMany(s => s.GetLoadableTypes())
+ .Where(t => type.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract)
+ .ToList();
+
+ // Create them and add.
+ this.ImageServices = availableTypes.Select(x => (Activator.CreateInstance(x) as IImageService)).ToList();
+
+ // Add the available settings.
+ foreach (IImageService imageService in this.ImageServices)
+ {
+ imageService.Settings = this.GetServiceSettings(imageService.GetType().Name);
+ }
+ }
+ catch (ReflectionTypeLoadException)
+ {
+ this.LoadImageServicesFromConfiguration();
+ }
+ }
+ else
+ {
+ this.LoadImageServicesFromConfiguration();
+ }
+ }
+ }
+
///
/// Loads graphics processors from configuration.
///
///
- /// Thrown when an cannot be loaded.
+ /// Thrown when an cannot be loaded.
///
private void LoadGraphicsProcessorsFromConfiguration()
{
@@ -353,6 +437,35 @@ namespace ImageProcessor.Web.Configuration
webProcessor.Processor.Settings = this.GetPluginSettings(webProcessor.GetType().Name);
}
}
+
+ ///
+ /// Loads image services from configuration.
+ ///
+ ///
+ /// Thrown when an cannot be loaded.
+ ///
+ private void LoadImageServicesFromConfiguration()
+ {
+ ImageSecuritySection.ServiceElementCollection services = imageSecuritySection.ImageServices;
+ this.ImageServices = new List();
+ foreach (ImageSecuritySection.ServiceElement config in services)
+ {
+ Type type = Type.GetType(config.Type);
+
+ if (type == null)
+ {
+ throw new TypeLoadException("Couldn't load IImageService: " + config.Type);
+ }
+
+ this.GraphicsProcessors.Add(Activator.CreateInstance(type) as IWebGraphicsProcessor);
+ }
+
+ // Add the available settings.
+ foreach (IImageService service in this.ImageServices)
+ {
+ service.Settings = this.GetServiceSettings(service.GetType().Name);
+ }
+ }
#endregion
}
}
\ No newline at end of file
diff --git a/src/ImageProcessor.Web/Configuration/ImageSecuritySection.cs b/src/ImageProcessor.Web/Configuration/ImageSecuritySection.cs
index c58b07579..814fee0b0 100644
--- a/src/ImageProcessor.Web/Configuration/ImageSecuritySection.cs
+++ b/src/ImageProcessor.Web/Configuration/ImageSecuritySection.cs
@@ -10,16 +10,14 @@
namespace ImageProcessor.Web.Configuration
{
- #region Using
using System;
using System.Configuration;
using System.IO;
+ using System.Linq;
using System.Xml;
using ImageProcessor.Web.Helpers;
- #endregion
-
///
/// Represents an image security section within a configuration file.
///
@@ -91,13 +89,13 @@ namespace ImageProcessor.Web.Configuration
/// Gets the
///
/// The
- [ConfigurationProperty("whiteList", IsRequired = true)]
- public WhiteListElementCollection WhiteList
+ [ConfigurationProperty("services", IsRequired = true)]
+ public ServiceElementCollection ImageServices
{
get
{
- object o = this["whiteList"];
- return o as WhiteListElementCollection;
+ object o = this["services"];
+ return o as ServiceElementCollection;
}
}
#endregion
@@ -125,6 +123,301 @@ namespace ImageProcessor.Web.Configuration
}
#endregion
+ ///
+ /// Represents a ServiceElement configuration element within the configuration.
+ ///
+ public class ServiceElement : ConfigurationElement
+ {
+ ///
+ /// Gets or sets the name of the plugin file.
+ ///
+ /// The name of the plugin.
+ [ConfigurationProperty("name", DefaultValue = "", IsRequired = true)]
+ public string Name
+ {
+ get { return (string)this["name"]; }
+
+ set { this["name"] = value; }
+ }
+
+ ///
+ /// Gets or sets the type of the service file.
+ ///
+ /// The full Type definition of the plugin
+ [ConfigurationProperty("type", DefaultValue = "", IsRequired = true)]
+ public string Type
+ {
+ get { return (string)this["type"]; }
+
+ set { this["type"] = value; }
+ }
+
+ ///
+ /// Gets the .
+ ///
+ ///
+ /// The .
+ ///
+ [ConfigurationProperty("settings", IsRequired = false)]
+ public SettingElementCollection Settings
+ {
+ get
+ {
+ return this["settings"] as SettingElementCollection;
+ }
+ }
+
+ ///
+ /// Gets the .
+ ///
+ ///
+ /// The .
+ ///
+ [ConfigurationProperty("whitelist", IsRequired = false)]
+ public WhiteListElementCollection WhiteList
+ {
+ get
+ {
+ return this["whitelist"] as WhiteListElementCollection;
+ }
+ }
+ }
+
+ ///
+ /// Represents a collection of elements within the configuration.
+ ///
+ public class ServiceElementCollection : ConfigurationElementCollection
+ {
+ ///
+ /// Gets or sets a value indicating whether to auto load all plugins.
+ /// Defaults to True.
+ ///
+ /// If True plugins are auto discovered and loaded from all assemblies otherwise they must be defined in the configuration file
+ [ConfigurationProperty("autoLoadPlugins", DefaultValue = true, IsRequired = false)]
+ public bool AutoLoadPlugins
+ {
+ get { return (bool)this["autoLoadPlugins"]; }
+
+ set { this["autoLoadPlugins"] = value; }
+ }
+
+ ///
+ /// Gets the type of the .
+ ///
+ ///
+ /// The of this collection.
+ ///
+ public override ConfigurationElementCollectionType CollectionType
+ {
+ get { return ConfigurationElementCollectionType.BasicMap; }
+ }
+
+ ///
+ /// Gets the name used to identify this collection of elements in the configuration file when overridden in a derived class.
+ ///
+ ///
+ /// The name of the collection; otherwise, an empty string. The default is an empty string.
+ ///
+ protected override string ElementName
+ {
+ get { return "service"; }
+ }
+
+ ///
+ /// Gets or sets the
+ /// at the specified index within the collection.
+ ///
+ /// The index at which to get the specified object.
+ ///
+ /// The
+ /// at the specified index within the collection.
+ ///
+ public ServiceElement this[int index]
+ {
+ get
+ {
+ return (ServiceElement)BaseGet(index);
+ }
+
+ set
+ {
+ if (this.BaseGet(index) != null)
+ {
+ this.BaseRemoveAt(index);
+ }
+
+ this.BaseAdd(index, value);
+ }
+ }
+
+ ///
+ /// When overridden in a derived class, creates a new .
+ ///
+ ///
+ /// A new .
+ ///
+ protected override ConfigurationElement CreateNewElement()
+ {
+ return new ServiceElement();
+ }
+
+ ///
+ /// Gets the element key for a specified configuration element when overridden in a derived class.
+ ///
+ ///
+ /// An that acts as the key for the specified .
+ ///
+ /// The to return the key for.
+ protected override object GetElementKey(ConfigurationElement element)
+ {
+ return ((ServiceElement)element).Name;
+ }
+ }
+
+ ///
+ /// Represents a SettingElement configuration element within the configuration.
+ ///
+ public class SettingElement : ConfigurationElement
+ {
+ ///
+ /// Gets or sets the key of the plugin setting.
+ ///
+ /// The key of the plugin setting.
+ [ConfigurationProperty("key", IsRequired = true, IsKey = true)]
+ public string Key
+ {
+ get
+ {
+ return this["key"] as string;
+ }
+
+ set
+ {
+ this["key"] = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the value of the plugin setting.
+ ///
+ /// The value of the plugin setting.
+ [ConfigurationProperty("value", IsRequired = true)]
+ public string Value
+ {
+ get
+ {
+ return (string)this["value"];
+ }
+
+ set
+ {
+ this["value"] = value;
+ }
+ }
+ }
+
+ ///
+ /// Represents a SettingElementCollection collection configuration element within the configuration.
+ ///
+ public class SettingElementCollection : ConfigurationElementCollection
+ {
+ ///
+ /// Gets the type of the .
+ ///
+ ///
+ /// The of this collection.
+ ///
+ public override ConfigurationElementCollectionType CollectionType
+ {
+ get { return ConfigurationElementCollectionType.BasicMap; }
+ }
+
+ ///
+ /// Gets the name used to identify this collection of elements in the configuration file when overridden in a derived class.
+ ///
+ ///
+ /// The name of the collection; otherwise, an empty string. The default is an empty string.
+ ///
+ protected override string ElementName
+ {
+ get { return "setting"; }
+ }
+
+ ///
+ /// Gets or sets the
+ /// at the specified index within the collection.
+ ///
+ /// The index at which to get the specified object.
+ ///
+ /// The the
+ /// at the specified index within the collection.
+ ///
+ public ImageProcessingSection.SettingElement this[int index]
+ {
+ get
+ {
+ return (ImageProcessingSection.SettingElement)BaseGet(index);
+ }
+
+ set
+ {
+ if (this.BaseGet(index) != null)
+ {
+ this.BaseRemoveAt(index);
+ }
+
+ this.BaseAdd(index, value);
+ }
+ }
+
+ ///
+ /// Returns the setting element with the specified key.
+ ///
+ /// the key representing the element
+ /// the setting element
+ public new ImageProcessingSection.SettingElement this[string key]
+ {
+ get { return (ImageProcessingSection.SettingElement)BaseGet(key); }
+ }
+
+ ///
+ /// Returns a value indicating whether the settings collection contains the
+ /// given object.
+ ///
+ /// The key to identify the setting.
+ /// True if the collection contains the key; otherwise false.
+ public bool ContainsKey(string key)
+ {
+ object[] keys = BaseGetAllKeys();
+
+ return keys.Any(obj => (string)obj == key);
+ }
+
+ ///
+ /// Gets the element key for a specified PluginElement configuration element.
+ ///
+ ///
+ /// The ConfigurationElement
+ /// to return the key for.
+ ///
+ /// The element key for a specified PluginElement configuration element.
+ protected override object GetElementKey(ConfigurationElement element)
+ {
+ return ((ImageProcessingSection.SettingElement)element).Key;
+ }
+
+ ///
+ /// Creates a new SettingElement configuration element.
+ ///
+ ///
+ /// A new SettingElement configuration element.
+ ///
+ protected override ConfigurationElement CreateNewElement()
+ {
+ return new ImageProcessingSection.SettingElement();
+ }
+ }
+
///
/// Represents a whitelist collection configuration element within the configuration.
///
diff --git a/src/ImageProcessor.Web/ImageProcessor.Web.csproj b/src/ImageProcessor.Web/ImageProcessor.Web.csproj
index 47b69f8ac..05b2299b1 100644
--- a/src/ImageProcessor.Web/ImageProcessor.Web.csproj
+++ b/src/ImageProcessor.Web/ImageProcessor.Web.csproj
@@ -46,6 +46,7 @@
+
@@ -86,6 +87,8 @@
+
+
diff --git a/src/ImageProcessor.Web/Services/IImageService.cs b/src/ImageProcessor.Web/Services/IImageService.cs
new file mode 100644
index 000000000..07b5d43fc
--- /dev/null
+++ b/src/ImageProcessor.Web/Services/IImageService.cs
@@ -0,0 +1,51 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Defines properties and methods for allowing retrieval of image from different means.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Web.Services
+{
+ using System.Collections.Generic;
+ using System.Threading.Tasks;
+
+ ///
+ /// Defines properties and methods for allowing retrieval of image from different means.
+ ///
+ public interface IImageService
+ {
+ ///
+ /// Gets the key for the given implementation.
+ ///
+ /// This value is used as a prefix for any image requests that should use this service.
+ ///
+ ///
+ string Key { get; }
+
+ ///
+ /// Gets a value indicating whether the image service requests files from
+ /// the locally based file system.
+ ///
+ bool IsFileLocalService { get; }
+
+ ///
+ /// Gets or sets any additional settings required by the service.
+ ///
+ Dictionary Settings { get; set; }
+
+ ///
+ /// Gets the image using the given identifier.
+ ///
+ ///
+ /// The value identifying the image to fetch.
+ ///
+ ///
+ /// The array containing the image data.
+ ///
+ Task GetImage(object id);
+ }
+}
diff --git a/src/ImageProcessor.Web/Services/LocalFileImageService.cs b/src/ImageProcessor.Web/Services/LocalFileImageService.cs
new file mode 100644
index 000000000..b961f6455
--- /dev/null
+++ b/src/ImageProcessor.Web/Services/LocalFileImageService.cs
@@ -0,0 +1,86 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// The local file image service for retrieving images from the file system.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Web.Services
+{
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Threading.Tasks;
+ using System.Web;
+
+ ///
+ /// The local file image service for retrieving images from the file system.
+ ///
+ public class LocalFileImageService : IImageService
+ {
+ ///
+ /// Gets the key for the given implementation.
+ ///
+ /// This value is used as a prefix for any image requests that should use this service.
+ ///
+ ///
+ public string Key
+ {
+ get
+ {
+ return string.Empty;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the image service requests files from
+ /// the locally based file system.
+ ///
+ public bool IsFileLocalService
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ ///
+ /// Gets or sets any additional settings required by the service.
+ ///
+ public Dictionary Settings { get; set; }
+
+ ///
+ /// Gets the image using the given identifier.
+ ///
+ ///
+ /// The value identifying the image to fetch.
+ ///
+ ///
+ /// The array containing the image data.
+ ///
+ public async Task GetImage(object id)
+ {
+ string path = id.ToString();
+ byte[] buffer;
+
+ // Check to see if the file exists.
+ // ReSharper disable once AssignNullToNotNullAttribute
+ FileInfo fileInfo = new FileInfo(path);
+
+ if (!fileInfo.Exists)
+ {
+ throw new HttpException(404, "No image exists at " + path);
+ }
+
+ using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
+ {
+ buffer = new byte[file.Length];
+ await file.ReadAsync(buffer, 0, (int)file.Length);
+ }
+
+ return buffer;
+ }
+ }
+}
diff --git a/src/ImageProcessor.Web/Services/RemoteImageService.cs b/src/ImageProcessor.Web/Services/RemoteImageService.cs
new file mode 100644
index 000000000..55bb14d2d
--- /dev/null
+++ b/src/ImageProcessor.Web/Services/RemoteImageService.cs
@@ -0,0 +1,105 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// The remote image service.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Web.Services
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Net;
+ using System.Threading.Tasks;
+
+ using ImageProcessor.Web.Helpers;
+
+ ///
+ /// The remote image service.
+ ///
+ public class RemoteImageService : IImageService
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public RemoteImageService()
+ {
+ this.Settings = new Dictionary();
+ }
+
+ ///
+ /// Gets the key for the given implementation.
+ ///
+ /// This value is used as a prefix for any image requests that should use this service.
+ ///
+ ///
+ public string Key
+ {
+ get
+ {
+ return "remote.axd";
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the image service requests files from
+ /// the locally based file system.
+ ///
+ public bool IsFileLocalService
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Gets or sets any additional settings required by the service.
+ ///
+ public Dictionary Settings { get; set; }
+
+ ///
+ /// Gets the image using the given identifier.
+ ///
+ ///
+ /// The value identifying the image to fetch.
+ ///
+ ///
+ /// The array containing the image data.
+ ///
+ public async Task GetImage(object id)
+ {
+ Uri uri = new Uri(id.ToString());
+ RemoteFile remoteFile = new RemoteFile(uri, false);
+ byte[] buffer = { };
+
+ // Prevent response blocking.
+ WebResponse webResponse = await remoteFile.GetWebResponseAsync().ConfigureAwait(false);
+
+ using (MemoryStream memoryStream = new MemoryStream())
+ {
+ using (WebResponse response = webResponse)
+ {
+ using (Stream responseStream = response.GetResponseStream())
+ {
+ if (responseStream != null)
+ {
+ responseStream.CopyTo(memoryStream);
+
+ // Reset the position of the stream to ensure we're reading the correct part.
+ memoryStream.Position = 0;
+
+ buffer = memoryStream.ToArray();
+ }
+ }
+ }
+ }
+
+ return buffer;
+ }
+ }
+}