ASP.NET Web API – Upload & Retrieve Images from MS SQL Database

Overview

This is an example to create a table to store user profile images inside a column labeled ImageData of type image. Then build 2 API actions to add and retrieve the Image and Meta Data.

First create the table in MS SQL with SSMS.

CREATE TABLE [dbo].[UserImages](
[ImageId] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] NULL,
[Label] varchar NULL,
[Size] [int] NULL,
[Url] varchar NULL,
[MimeType] nchar NULL,
[BinaryData] varbinary NULL,
[ImageData] [image] NULL,
[IsActive] [bit] NULL,
[DateUploaded] [date] NULL,
CONSTRAINT [PK_Images] PRIMARY KEY CLUSTERED
(
[ImageId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

Storing the image

Import the following

using System;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Http.Cors;

Create a Controller

Remember to enable CORS if you plan to use a client side application such as React, Angular, or Vue.

namespace YOURAPPNAME.Controllers
{
[EnableCors(origins: "", headers: "", methods: "*")]
[RoutePrefix("api/images")]
public class UserImageController : ApiController
{

Upload Action

Now add the action to upload a file to the Database. The action should be asynchronous using Async Task

[HttpPost]
[Route("addtodb")]
public async Task addImageToDatabase()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var ctx = HttpContext.Current;
var root = ctx.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
DateTime today = DateTime.Today;

Get the Mime Types with a Switch Statement.

NOTE: The folder you pick must exist on the server the API is deployed to. In this example APP_DATA is used but it will not exist on the server. You can use “~/Content” instead or add a folder to the app after it’s deployed. Make sure the folder ahs read write access.

string catStr = formdata["MimeType"];
switch (catStr)
{
case "image/jpg":
uniqueFileName = $@"{Guid.NewGuid()}.jpg";
break;
case "image/jpeg":
uniqueFileName = $@"{Guid.NewGuid()}.jpg";
break;
case "image/gif":
uniqueFileName = $@"{Guid.NewGuid()}.gif";
break;
case "image/png":
uniqueFileName = $@"{Guid.NewGuid()}.png";
break;
case "image/tiff":
uniqueFileName = $@"{Guid.NewGuid()}.tif";
break;
case "image/webp":
uniqueFileName = $@"{Guid.NewGuid()}.webp";
break;
case "image/apng":
uniqueFileName = $@"{Guid.NewGuid()}.apng";
break;
}

Entity Frameworks

For the database with EF (Entity Frameworks) part of the action

UserImage ProfileForm = new UserImage(); ProfileForm.UserId = int.Parse(formdata["UserId"]); ProfileForm.IsActive = true; ProfileForm.IsActive = true; ProfileForm.Label = formdata["Label"]; ProfileForm.DateCreated = today;

Parsing the File Array

For the file portion of the action loops thru all files attached to the request and gets and put the binary, size, and MimeType into their respective fields.

foreach (MultipartFileData file in provider.FileData)
{
var fileName = file.Headers.ContentDisposition.FileName.Replace("\"", string.Empty);
string mimeType = MimeMapping.GetMimeMapping(fileName);
byte[] documentData = File.ReadAllBytes(file.LocalFileName); ProfileForm.ImageData = documentData;
ProfileForm.MimeType = mimeType;
ProfileForm.Size = documentData.Length;
}

All Together

[HttpPost]
[Route("addtodb")]
public async Task addImageToDatabase()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var ctx = HttpContext.Current;
var root = ctx.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
DateTime today = DateTime.Today; 
try { 
using (portfolioEntities db = new portfolioEntities()) { 
// Read the form data. await   Request.Content.ReadAsMultipartAsync(provider);
 var uniqueFileName = ""; NameValueCollection formdata = provider.FormData; 
UserImage ProfileForm = new UserImage(); 
ProfileForm.UserId = int.Parse(formdata["UserId"]); ProfileForm.IsActive = true; 
ProfileForm.IsActive = true; 
ProfileForm.Label = formdata["Label"]; 
ProfileForm.DateCreated = today; 
foreach (MultipartFileData file in provider.FileData) 
{ 
var fileName = file.Headers.ContentDisposition.FileName.Replace("\"", string.Empty); 
string mimeType = MimeMapping.GetMimeMapping(fileName); 
byte[] documentData = File.ReadAllBytes(file.LocalFileName); ProfileForm.ImageData = documentData; 
ProfileForm.MimeType = mimeType; 
ProfileForm.Size = documentData.Length;
} 
db.UserImages.Add(ProfileForm); 
db.SaveChanges(); 
return Ok("Successfully Added Image To Database"); 
} 
} catch (System.Exception e) 
{ 
      return BadRequest(e.Message); }
}

Using Postman call the API Action as follows with your LocalHost Address. Remember use the POST method

From SSMS you can check the new entries for the UserImages table. Please ignore the extra fields.

Getting the image

Now create another action called getdbimage and allow an ID to be passed as a parameter to get a single image record. This is simple get from the database with Entity Framework using a Where clause to get the images for a User with UserId.

[HttpGet]
[Route("getdbimagelist")]
public object getDBImageList()
{
    try
    {
        using (DemoEntities db = new DemoEntities())
        {

            var queryResults = db.UserImages.ToList();

            return new { status = StatusCodes.OK.code, msg = StatusCodes.OK.msg, data = queryResults };
        }
    }
    catch (System.Exception e)
    {
        return new { status = StatusCodes.NotFound.code, msg = e.InnerException, data = 0 };
    }
}

Using Postman GET request. Remember this is a GET request