Introduction
This article is on how to upload and retrieve images from your .NET Core 2.1 API. The images can be accessed using the image name which is displayed on the browser.
I have also uploaded the code on Github. Hope you find this guide useful.
Background
- I used postman to make
POST
requests, any alternative should be fine - .NET Core 2.1 compatible Visual Studio
- An idea about dependency injection
- A good knowledge about
Task<>
and async
methods - Some idea about API controllers (I have explained on a very high level which is enough to understand what is going on)
Code
First up, let’s create a new ASP .NET Core Web application and select API project and call it ImageUploader
.
Project Structure
Below is the project structure that I have used to create the project. I will be explaining what each of the files do:
The Handler folder has a ImageHandler.cs which is used by the ImageController
to call the image writing component of the program by injecting the handler into the controller.
The ImageWriter
is a .NET Core 2.1 class library project which takes care of writing and the image to the disk. We write the image in wwwroot/images folder which I have created.
The WriterHelper.cs is a helper class used by the ImageWriter
to validate whether a given file is an image file.
Code
I would like to start with the WriterHelper.cs below which is the code to validate the uploaded file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ImageWriter.Helper
{
public class WriterHelper
{
public enum ImageFormat
{
bmp,
jpeg,
gif,
tiff,
png,
unknown
}
public static ImageFormat GetImageFormat(byte[] bytes)
{
var bmp = Encoding.ASCII.GetBytes("BM");
var gif = Encoding.ASCII.GetBytes("GIF");
var png = new byte[] { 137, 80, 78, 71 };
var tiff = new byte[] { 73, 73, 42 };
var tiff2 = new byte[] { 77, 77, 42 };
var jpeg = new byte[] { 255, 216, 255, 224 };
var jpeg2 = new byte[] { 255, 216, 255, 225 };
if (bmp.SequenceEqual(bytes.Take(bmp.Length)))
return ImageFormat.bmp;
if (gif.SequenceEqual(bytes.Take(gif.Length)))
return ImageFormat.gif;
if (png.SequenceEqual(bytes.Take(png.Length)))
return ImageFormat.png;
if (tiff.SequenceEqual(bytes.Take(tiff.Length)))
return ImageFormat.tiff;
if (tiff2.SequenceEqual(bytes.Take(tiff2.Length)))
return ImageFormat.tiff;
if (jpeg.SequenceEqual(bytes.Take(jpeg.Length)))
return ImageFormat.jpeg;
if (jpeg2.SequenceEqual(bytes.Take(jpeg2.Length)))
return ImageFormat.jpeg;
return ImageFormat.unknown;
}
}
}
Now, it’s time to set up the actual ImageWriter.cs but before that, let’s populate the IImageWriter.cs interface so we can inject it in our handlers.
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace ImageWriter.Interface
{
public interface IImageWriter
{
Task<string> UploadImage(IFormFile file);
}
}
And now the ImageWriter.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using ImageWriter.Helper;
using ImageWriter.Interface;
using Microsoft.AspNetCore.Http;
namespace ImageWriter.Classes
{
public class ImageWriter : IImageWriter
{
public async Task<string> UploadImage(IFormFile file)
{
if (CheckIfImageFile(file))
{
return await WriteFile(file);
}
return "Invalid image file";
}
private bool CheckIfImageFile(IFormFile file)
{
byte[] fileBytes;
using (var ms = new MemoryStream())
{
file.CopyTo(ms);
fileBytes = ms.ToArray();
}
return WriterHelper.GetImageFormat(fileBytes) != WriterHelper.ImageFormat.unknown;
}
public async Task<string> WriteFile(IFormFile file)
{
string fileName;
try
{
var extension = "." + file.FileName.Split('.')[file.FileName.Split('.').Length - 1];
fileName = Guid.NewGuid().ToString() + extension;
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\images", fileName);
using (var bits = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(bits);
}
}
catch (Exception e)
{
return e.Message;
}
return fileName;
}
}
}
The main logic here is to first check the file. If it is an image file, we write it with a new name for security reasons and return the name back to the user (we can also use a persistent storage to map the file name to the new name for later retrieval). If file is invalid, we return an error message.
With the main functionalities being done, let’s head over to the handler where we direct the incoming requests to an appropriate method.
using System.Threading.Tasks;
using ImageWriter.Interface;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace ImageUploader.Handler
{
public interface IImageHandler
{
Task<IActionResult> UploadImage(IFormFile file);
}
public class ImageHandler : IImageHandler
{
private readonly IImageWriter _imageWriter;
public ImageHandler(IImageWriter imageWriter)
{
_imageWriter = imageWriter;
}
public async Task<IActionResult> UploadImage(IFormFile file)
{
var result = await _imageWriter.UploadImage(file);
return new ObjectResult(result);
}
}
}
Here, ImageWriter
is injected into the handler.
Time to configure the controller:
using System.Threading.Tasks;
using ImageUploader.Handler;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace ImageUploader.Controllers
{
[Route("api/image")]
public class ImagesController : Controller
{
private readonly IImageHandler _imageHandler;
public ImagesController(IImageHandler imageHandler)
{
_imageHandler = imageHandler;
}
public async Task<IActionResult> UploadImage(IFormFile file)
{
return await _imageHandler.UploadImage(file);
}
}
}
Finally, we have to configure the Startup.cs to inform the application to use wwwroot/images folder. We can do that by adding a simple one line of code in our Configure
method.
We can also make reference to files outside the wwwroot folder by specifying the path of the inside of the commented area of the above screenshot. In the screenshot above, the commented configuration would use a folder called StaticFiles inside the main project.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseMvc();
}
Finally, we add the dependency injection for the ImageHandler
and ImageWriter
in our ConfigureServices
method.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddTransient<IImageHandler, ImageHandler>();
services.AddTransient<ImageWriter.Interface.IImageWriter,
ImageWriter.Classes.ImageWriter>();
}
Result
Let’s fire it up and send an image file using postman:
Make sure you set the key to file and value to a valid image file. Also ensure that the request is a POST
request. As we can see from the above screenshot, we got the name of the uploaded file. Let’s check if it is actually inside the images folder:
Now how do we retrieve it? It is very simple, all we have to do is call the http://ipaddress:port/images/filename.
There you have it! An API to upload and retrieve images. You can use the above link to directly embed images into HTML <img src =””>
tags.