Returning Images from ASP.NET Web API

Introduction

Sometimes you need to save and retrieve image data in SQL Server as a part of Web API functionality. A common approach is to save images as physical image files on the web server and then store the image URL in a SQL Server database. However, at times you need to store image data directly into a SQL Server database rather than the image URL. While dealing with the later scenario you need to read images from a database and then return this image data from your Web API. This article shows the steps involved in this process.

SQL Server Database and EF Data Model

An image is a binary piece of data. When it comes to storing images directly into a SQL Server database, developer's use Image or Varbinary data types for such columns. To save and retrieve image data from your .NET applications (including Web API) you need to use byte arrays. Let's try to understand this with a simple example. Begin by creating a new ASP.NET MVC project and select template as Web API. Then add a new SQL Server database in the App_Data folder. Name the database as ImageDb.

Add New Item
Add New Item

Then add a table in the newly added SQL Server database matching the following schema.

Add a New Table
Add a New Table

The Images table shown above has two columns - Id and ImageData. Notice that data type of the ImageData column is varbinary(MAX). The varbinary data type can take any binary data including images and files.

Now add an Entity Framework data model for the ImageDb database in the Models folder.

Entity Framework Data Model
Entity Framework Data Model

You will notice in the properties window of the ImageData column that its data type is set to Binary. 

Uploading Image Files

Although our main interest is to see how images are returned from an ASP.NET Web API, you need to add a few sample images inside the Images table that you can retrieve later. This requires that you upload files from the client machine and store them in the Images table. To do so, add Index view for the Index() action method of the HomeController (this controller class is added by default when you create a new Web API project). The HTML markup of the view is shown below:

<body>
    <form id="form1">
        <input type="file" id="file1" multiple="multiple" />
        <input type="button" id="btnUpload" value="Upload" />
        &nbsp;&nbsp;&nbsp;
        <input type="button" id="btnShow" value="Show" />
        <br />
        <br />
        <div id="imgContainer"></div>
    </form>
</body>

As you can see the Index view contains a file input field. Notice that the multiple attribute of the file input field is set so that you can upload multiple files at a time. The btnUpload triggers the file upload process. The other HTML elements (btnShow and imgContainer) are used during image retrieval process.

Now, add the following jQuery code in the click event handler of the btnUpload button.

$("#btnUpload").click(function () {
  var files = $("#file1").get(0).files;
  if (files.length > 0) {
    var data = new FormData();
    for (i = 0; i < files.length; i++) {
      data.append("file" + i, files[i]);
    }
  $.ajax({
      type: "POST",
      url: "/api/images",
      contentType: false,
      processData: false,
      data: data,
      success: function (results) {
        for (i = 0; i < results.length; i++) {
         alert(results[i]);
       }
     }
  });
  }
 }
}); 

We won't go into much details of this code here. If you are not familiar with file upload using Web API read this article to learn more. This code basically makes a POST request to the Images Web API and passes selected files as FormData object. You will develop the Images Web API shortly.

Now, open the default ApiController class from the Controllers folder and rename it to ImagesController. Then add the following Post() method to the ImagesController class.

public Task<IEnumerable<string>> Post()
{
  if (Request.Content.IsMimeMultipartContent())
  {
    string fullPath = HttpContext.Current.Server.MapPath("~/uploads");
    MultipartFormDataStreamProvider streamProvider = new MultipartFormDataStreamProvider(fullPath);
    var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith(t =>
    {
      if (t.IsFaulted || t.IsCanceled)
        throw new HttpResponseException(HttpStatusCode.InternalServerError);
      var fileInfo = streamProvider.FileData.Select(i =>
      {
        var info = new FileInfo(i.LocalFileName);
        ImageDbEntities db = new ImageDbEntities();
        Image img = new Image();
        img.ImageData = File.ReadAllBytes(info.FullName);
        db.Images.Add(img);
        db.SaveChanges();
        return "File uploaded successfully!";
      });
     return fileInfo;
    });
    return task;
  }
  else
  {
    throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "Invalid Request!"));
  }
}

The Post() Web API method basically grabs the files uploaded by the end user and saves them into the Images table. The Post() method uses asynchronous processing as indicated by the Task object. Notice the code marked in bold letters. It creates an instance of Image model class and sets its ImageData property. As mentioned earlier ImageData is a byte array. To read data from the uploaded file into a byte array you use the File.ReadAllBytes() method. The newly created Image object is then added to Images and SaveChanges() is called. The Post() method returns an IEnumerable of strings, each string is a success message in this case.

Now, run the project, select a few image files and upload them in the Images table. 

Retrieving Image Files

Now, add a method - Get() - to the Web API as shown below:

 public List<int> Get()
{
  ImageDbEntities db = new ImageDbEntities();
  var data = from i in db.Images
             select i.Id;
  return data.ToList();
}

The Get() method takes no parameters and returns a generic List of integers. These integers are nothing but image IDs.

Next, add another Get() method, this time accepting an id parameter.

public HttpResponseMessage Get(int id)
{
  ImageDbEntities db = new ImageDbEntities();
  var data = from i in db.Images
             where i.Id == id
             select i;
  Image img = (Image)data.SingleOrDefault();
  byte[] imgData = img.ImageData;
  MemoryStream ms = new MemoryStream(imgData);
  HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
  response.Content = new StreamContent(ms);
  response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
  return response;
}

Notice that the return type of the above Get() method is HttpResponseMessage. The HttpResponseMessage class from System.Net.Http represents an HTTP response that includes the HTTP status code and the response data. The Get() method accepts an Image id that is to be retrieved. Inside, it fetches the Image from the database and puts the ImageData in a byte array. A new MemoryStream is then created based on this byte array. A new instance of HttpResponseMessage is created with an HTTP status code of OK. The Content property of the HttpResponseMessage represents the content of the HTTP response. In this case the Content property is set to a new StreamContent object. The MemoryStream is passed in the constructor of the StreamContent object. The ContentType header of the response message is set to image/png using the MediaTypeHeaderValue. Finally, the HttpResponseMessage instance is returned from the Get() method.

Now, add the following jQuery code in the Index view.

$("#btnShow").click(function () {
  var options = {};
  options.url = "api/images";
  options.type = "GET";
  options.dataType = "json";
  options.contentType = "application/json";
  options.success = function (results) {
  $("#imgContainer").empty();
    for (var i = 0; i < results.length; i++) {
      $("#imgContainer").append("<img src='" + "api/images/" + results[i] + "' /> <br />");
    }
  };
  options.error = function (err) { alert(err.statusText); };
  $.ajax(options);
});

The click event handler of the Show button makes an Ajax request to the Get() method. Recollect that this Get() method returns a generic List of integers. In the success function a for loop iterates through all the Image ids and appends an <img> element to the imgContainer. Notice how the src attribute of the <img> tag is specified. It is of the form api/images/<image_id>. This way when the src attribute is set it actually invokes the Get() method of the Web API along with the Image id value. The Get() method returns an HttpResponseMessage whose content is nothing but the actual image data. Thus specifying the src attribute in this fashion displays the corresponding image. The following figure shows a sample run of the application.

 Sample Run
Sample Run

Summary

In order to return images stored directly in a SQL Server database from an ASP.NET Web API you need to create a Get() method that returns HttpResponseMessage. The Content property of the HttpResponseMessage class represents the binary image data associated with an image. In your HTML markup you can use image URLs that trigger the Get() method by passing an image ID. Using the approach illustrated in this article you can develop a Web API to store and retrieve images to and from a SQL server database. 



Related Articles

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: May 7, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT This eSeminar will explore three popular games engines and how they empower developers to create exciting, graphically rich, and high-performance games for Android® on Intel® Architecture. Join us for a deep dive as experts describe the features, tools, and common challenges using Marmalade, App Game Kit, and Havok game engines, as well as a discussion of the pros and cons of each engine and how they fit into your development …

  • IT departments are embracing cloud backup, but there's a lot you need to know before choosing a service provider. For example, did you know: That encryption doesn't guarantee privacy? That you could lose data by choosing the wrong cloud? That cloud backup doesn't have to be slow? Learn all the critical things you need to know by accessing the white paper, 5 Things You Didn't Know About Cloud Backup.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds