The goal is to use a database to store images and use MVC to call those images, using custom routes.

The premises are,
  1. The URL must be something like this: “imagebank/sample-file” or “imagebank/32403404303“.
  2. The MVC Controller/Action will get the image by an ID “sample-file” or “32403404303” and find out on some cache and/or database to display the image. If it exists in cache, get from cache if not get from database. So in HTML, we can call the image like this.

    1. <img src="~/imagebank/sample-file" />

  3. If you want to use another URL, for instance “foo/sample-file”, you can change the image bank route name in web.config.
  4. If you do not want to display the image and just download the file, use - “imagebank/sample-file/download“.
So, let's get started!
The image bank route configuration
App_Start\RouteConfig.cs
  1. public class RouteConfig
  2. {
  3. public static void RegisterRoutes(RouteCollection routes)
  4. {
  5. routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
  6. routes.MapRoute(
  7. name: "ImageBank",
  8. url: GetImageBankRoute() + "/{fileId}/{action}",
  9. defaults: new { controller = "ImageBank", action = "Index" }
  10. );
  11. routes.MapRoute(
  12. name: "Default",
  13. url: "{controller}/{action}/{id}",
  14. defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
  15. );
  16. }
  17. private static string GetImageBankRoute()
  18. {
  19. var key = "imagebank:routeName";
  20. var config = ConfigurationManager.AppSettings.AllKeys.Contains(key) ? ConfigurationManager.AppSettings.Get(key) : "";
  21. return config ?? "imagebank";
  22. }
  23. }
The Image Bank Controller
Controllers\ImageBankController.cs
  1. public class ImageBankController : Controller
  2. {
  3. public ImageBankController()
  4. {
  5. Cache = new Cache();
  6. Repository = new Repository();
  7. }
  8. public ActionResult Index(string fileId, bool download = false)
  9. {
  10. var defaultImageNotFound = "pixel.gif";
  11. var defaultImageNotFoundPath = $"~/content/img/{defaultImageNotFound}";
  12. var defaultImageContentType = "image/gif";
  13. var cacheKey = string.Format("imagebankfile_{0}", fileId);
  14. Models.ImageFile model = null;
  15. if (Cache.NotExists(cacheKey))
  16. {
  17. model = Repository.GetFile(fileId);
  18. if (model == null)
  19. {
  20. if (download)
  21. {
  22. return File(Server.MapPath(defaultImageNotFoundPath), defaultImageContentType, defaultImageNotFound);
  23. }
  24. return File(Server.MapPath(defaultImageNotFoundPath), defaultImageContentType);
  25. }
  26. Cache.Insert(cacheKey, "Default", model);
  27. }
  28. else
  29. {
  30. model = Cache.Get(cacheKey) as Models.ImageFile;
  31. }
  32. if (download)
  33. {
  34. return File(model.Body, model.ContentType, string.Concat(fileId, model.Extension));
  35. }
  36. return File(model.Body, model.ContentType);
  37. }
  38. public ActionResult Download(string fileId)
  39. {
  40. return Index(fileId, true);
  41. }
  42. private Repository Repository { get; set; }
  43. private Cache Cache { get; set; }
  44. }
The above code has two actions - one for displaying the image and the other for downloading it.

The database repository
Repository.cs
  1. public class Repository
  2. {
  3. public static Models.ImageFile GetFile(string fileId)
  4. {
  5. //Just an example, use you own data repository and/or database
  6. SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ImageBankDatabase"].ConnectionString);
  7. try
  8. {
  9. connection.Open();
  10. var sql = @"SELECT *
  11. FROM dbo.ImageBankFile
  12. WHERE FileId = @fileId
  13. OR ISNULL(AliasId, FileId) = @fileId";
  14. var command = new SqlCommand(sql, connection);
  15. command.Parameters.Add("@fileId", SqlDbType.VarChar).Value = fileId;
  16. command.CommandType = CommandType.Text;
  17. var ada = new SqlDataAdapter(command);
  18. var dts = new DataSet();
  19. ada.Fill(dts);
  20. var model = new Models.ImageFile();
  21. model.Extension = dts.Tables[0].Rows[0]["Extension"] as string;
  22. model.ContentType = dts.Tables[0].Rows[0]["ContentType"] as string;
  23. model.Body = dts.Tables[0].Rows[0]["FileBody"] as byte[];
  24. return model;
  25. }
  26. catch
  27. {
  28. }
  29. finally
  30. {
  31. if (connection != null)
  32. {
  33. connection.Close();
  34. connection.Dispose();
  35. connection = null;
  36. }
  37. }
  38. return null;
  39. }
  40. }
The repository is very simple. This code is just for demonstration. You can implement your own code.
The image bank model class
Models\ImageFile.cs
  1. public class ImageFile
  2. {
  3. public byte[] Body { get; set; }
  4. public string ContentType { get; set; }
  5. public string Extension { get; set; }
  6. }
Create table script
  1. USE [ImageBankDatabase]
  2. GO
  3. /****** Object: Table [dbo].[ImageBankFile] Script Date: 11/16/2016 12:36:56 ******/
  4. SET ANSI_NULLS ON
  5. GO
  6. SET QUOTED_IDENTIFIER ON
  7. GO
  8. SET ANSI_PADDING ON
  9. GO
  10. CREATE TABLE [dbo].[ImageBankFile](
  11. [FileId] [nvarchar](50) NOT NULL,
  12. [AliasId] [nvarchar](100) NULL,
  13. [FileBody] [varbinary](max) NULL,
  14. [Extension] [nvarchar](5) NULL,
  15. [ContentType] [nvarchar](50) NULL,
  16. CONSTRAINT [PK_ImageBankFile] PRIMARY KEY CLUSTERED
  17. (
  18. [FileId] ASC
  19. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
  20. ) ON [PRIMARY]
  21. GO
  22. SET ANSI_PADDING OFF
  23. GO
The Cache provider class
Cache.cs
  1. public class Cache
  2. {
  3. public Cache()
  4. {
  5. _config = ConfigurationManager.GetSection("system.web/caching/outputCacheSettings") as OutputCacheSettingsSection;
  6. }
  7. private OutputCacheSettingsSection _config;
  8. private OutputCacheProfile GetProfile(string profile)
  9. {
  10. return !string.IsNullOrEmpty(profile) ? _config.OutputCacheProfiles[profile] : new OutputCacheProfile("default");
  11. }
  12. private object GetFromCache(string id)
  13. {
  14. if (string.IsNullOrEmpty(id)) throw new NullReferenceException("id is null");
  15. if (System.Web.HttpRuntime.Cache != null)
  16. {
  17. lock (this)
  18. {
  19. return System.Web.HttpRuntime.Cache[id];
  20. }
  21. }
  22. return null;
  23. }
  24. public Cache Insert(string id, string profile, object obj)
  25. {
  26. if (System.Web.HttpRuntime.Cache != null)
  27. {
  28. if (string.IsNullOrEmpty(id))
  29. {
  30. throw new ArgumentNullException("id", "id is null");
  31. }
  32. if (string.IsNullOrEmpty(profile))
  33. {
  34. throw new ArgumentNullException("profile", string.Format("profile is null for id {0}", id));
  35. }
  36. var objProfile = GetProfile(profile);
  37. if (objProfile == null)
  38. {
  39. throw new NullReferenceException(string.Format("profile is null for id {0} and profile {1}", id, profile));
  40. }
  41. lock (this)
  42. {
  43. System.Web.HttpRuntime.Cache.Insert(id, obj, null, DateTime.Now.AddSeconds(objProfile.Duration), TimeSpan.Zero);
  44. }
  45. }
  46. return this;
  47. }
  48. public bool NotExists(string id)
  49. {
  50. return GetFromCache(id) == null;
  51. }
  52. public Cache Remove(string id)
  53. {
  54. if (System.Web.HttpRuntime.Cache != null)
  55. {
  56. lock (this)
  57. {
  58. System.Web.HttpRuntime.Cache.Remove(id);
  59. }
  60. }
  61. return this;
  62. }
  63. public object Get(string id)
  64. {
  65. return GetFromCache(id);
  66. }
  67. }
So that's it! I hope you enjoyed! Download full source code from here.