Blazor WebAssembly: Uploading Image to ASP.NET Core 5 API from Blazor WebAssembly Project

In this tutorial, we will see the mechanism of uploading an image from Blazor WebAssembly client application to ASP.NET Core 5 API. The file uploading is one of the most demanded features by the developer community. In the software applications, e.g. data collection application, social media types applications, etc. the image uploading is required frequently. In the modern web applications developed using ASP.NET Core 5, we can implement this using  the IFormFile interface. This interface can be used to upload the file using HTTP Request. This interface represents a file sent with the HttpRequest. This interface is available in the Microsoft.AspNetCore.Http.Features assembly and its Microsoft.AspNetCore.Http namespace. The IFromFile supports Buffering that reads the entire file. This file is further processed and saved on the server. 

The client application will be used as the Blazor WebAssembly application. This executes in browser by having the .NET runtime and project dependencies loaded in the browser. The Blazor WebAssembly application executes as a standalone application in the browser. 

The figure 1, shows the approach of uploading file to ASP.NET Core 5 API from the Blazor WebAssembly project



Figure 1: The File Uploading using Blazor WebAssembly Application

To implement the code for the tutorial, I have used Visual Studio Enterprise 2022 Preview. You can use the  Visual Studio 2019 with .NET 5 SDK. 

Step 1: Open Visual Studio and create a new ASP.NET Core 5 API application. Name this application as Core_API_ImageUploader. Make sure that the target framework selected as .NET 5 and security is None.

Step 2: In this project, add  a new folder and name this folder as Files. This folder will be used to store all uploaded files. 

Step 3: In this project, in the Controllers folder, add a new empty API controller and name this controller as FileUploadController. In this controller add the code as shown in listing 1

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace Core_API_ImageUploader.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class FileUploadController : ControllerBase
    {
        
        [HttpPost("file/upload")]
        public async Task<IActionResult> Upload([FromForm]IFormFile file)
        {
            try
            {
                
                if (!CheckFileExtension(file))
                {
                    return BadRequest($"The File does not have an extension or it is not image. " +
                        $"The Expected extension is .jpg/.png/.bmp");
                }

                if (!CheckFileSize(file))
                {
                    return BadRequest($"The size of file is more than 10 mb, " +
                        $"please make sure that the file size must be less than 10 mb");
                }
                // Read the folder where the file is to be saved
                var folder = Path.Combine(Directory.GetCurrentDirectory(), "Files");
                 
                if (file.Length > 0)
                {
                   // REad the Uploaded File Name
                    var postedFileName = ContentDispositionHeaderValue
                      .Parse(file.ContentDisposition)
                        .FileName.Trim('"');
                    
                    // set the file path as FolderName/FileName
                    var finalPath = Path.Combine(folder, postedFileName);
                    using (var fs = new FileStream(finalPath, FileMode.Create))
                    {
                        await file.CopyToAsync(fs);
                    }
                    return Ok($"File is uploaded Successfully");
                }
                else
                {
                    return BadRequest("The File is not received.");
                }


            }
            catch (Exception ex)
            {
                return StatusCode(500, 
                  $"Some Error Occcured while uploading File {ex.Message}");
            }
        }
        
        /// The file extension must be jpg/bmp/png
       
        private bool CheckFileExtension(IFormFile file)
        {
            string[] extensions = new string[] { "jpg", "bmp", "png" };

            var fileNameExtension = file.FileName.Split(".")[1];
            if (string.IsNullOrEmpty(fileNameExtension) || 
                !extensions.Contains(fileNameExtension))
            {
                return false;
            }
           
            return true;
        }
        
        /// Check the file size, it must be less than 10 mb
        
        private bool CheckFileSize(IFormFile file)
        {
            if (file.Length > 1e+7)
            {
                return false;
            }
            return true;
        }
    }
}


Listing 1: The File Uploading Logic       

The controller shown in the listing 1, contains the Upload() action method. This method accepts IFormFile interface that represents the file send in HttpRequest. The Upload() method accepts the file and check file extension using the CheckFileExtension() method. This is a private method that makes sure that the file extension must be either as jpg, bmp or png. If the file has other different extension, then the BadRequestResult will be returned. The Upload() method further check the file size using the CheckFileSize() private method. If the file size is more than 10 MB then also it will not be accepted. If the posted file in Http request is image file and less than 10 MB then it will be accepted and saved in the Files folder.      

Step 4: Modify the ConfigureServices() method of the Startup class from Startup.cs file by adding the code for CORS policy, so that it can be accessed by client applications running in browser. The code is shown in listing 2

 services.AddCors(options => {
     options.AddPolicy("cors", policy => {
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader();
         });
 });


Listing 2: The CORS Service in ConfigureServices() method

Modify the Configure() method Startup class by adding Middlewares for using CORS service. Since, we will be storing the uploaded files in the Files folder, we need to use the Static Files Middleware to map to this folder as shown in listing 3

 app.UseCors("cors");
 app.UseStaticFiles();
 app.UseStaticFiles(new StaticFileOptions()
    {
     FileProvider = new PhysicalFileProvider
      (Path.Combine(Directory.GetCurrentDirectory(), @"Files")),
         RequestPath = new PathString("/Files")
     });


Listing 3: The Middleware configuration  

Note: Make sure that you set the port for running the API as 5000.

Adding the Blazor Web Assembly Client Project

Step 4:  In the same solution, add a new Blazor WebAssembly application. Name this application as Blazor_FileUploader. In this application, in the Pages folder add a new Razor Component and name it as FileUploader.razor.  In this component add the code as shown in this listing 4


@page "/fileuploader"
@using System.IO
@inject HttpClient httpClient
<h3>Upload File</h3>
<form @onsubmit="@UploadFile" enctype="multipart/form-data">
    <InputFile OnChange="@SelectFile"/>
    <input type="submit" value="Upload"> 
    <img src="@imgUrl">
</form>
@code {
    private StreamContent fileContent;
    private string fileName;
    private string imgUrl;
    private string imageType;
    protected override void OnInitialized()
    {
        base.OnInitialized();
    }

    private async Task UploadFile()
    {
      // Provides a container for content encoded using multipart/form-data MIME type.
      using var content = new MultipartFormDataContent();
      content.Add
      (content: fileContent,name: "\"file\"",fileName:fileName);
      var response = 
                await httpClient.PostAsync
                ("http://localhost:5000/api/FileUpload/file/upload", 
                content);
    }

    private async Task SelectFile(InputFileChangeEventArgs  e)
    {
       // setting the max size for the file 
       long maxFileSize = 1024 * 1024 * 10;
       // Provide the HTTP Content based Stream
       // and open the stream for reading the uploaded file
       fileContent = new StreamContent(e.File.OpenReadStream(maxFileSize));
       // read file name
       fileName = e.File.Name;

       var fileSize = new byte[e.File.Size];
       // read the file bytes in sequence
       await e.File.OpenReadStream().ReadAsync(fileSize);
       // read file content type
       imageType = e.File.ContentType;
       // create URL
       imgUrl = $"data:{imageType};base64,{Convert.ToBase64String(fileSize)}";

       this.StateHasChanged();
    }   
}

Listing 4: File Uploader Component Code   

The code in listing 4 has SelectFile() and UploadFile() methods. The SelectFile() method is bound to InputFile component to select the file. This method accepts the InputFileChangeEventsArgs parameter. This parameter represent the selected file using InputFile component. This method uses StreamContent class that is used to provide the HTTP content stream and open the stream for reading the uploaded file. The StreamContent() constructor accepts the Stream object created using OpenReadStreamAsync() method of the IBrowserFile interface. This interface represents that data of a file selected from the InputFile component. The SelectFile() further reads the bytes from file sequentially and then create file URL which we are storing in imgUrl string variable. The UploadFile() is bound to the onsubmit() event of the form. This method make the Http Post request to post the file to API.

Step 5: Modify the NavMenu.razor file in Shared folder to define routing for the FileUpload component as shown in listing 5

  <li class="nav-item px-3">
            <NavLink class="nav-link" href="fileuploader">
                <span class="oi oi-plus" aria-hidden="true"></span> Upload
            </NavLink>
        </li>


Listing 5: The Routing for FileUplaod component    

To run the application, make sure that the solution has multiple startup project. Set the API project to start first and then the Blazor application. Run the project, the API and Blazor application will be loaded in the browser as shown in figure 2



Figure 2: Running the application   

Click on the Upload link, this will show the FileUpload component. On this component clock on Choose File button, this will open the   Open Dialog box, select the image file from the Open File dialog and click on Open button. The selected file will be displayed in browser. Click on the Upload button, this will upload the image and show the image in the browser as shown in figure 3



Figure 3: The File selected to upload 

The uploaded file can be seen in the Files folder of the API project.

The code for this article can be downloaded from git link here

Conclusion: The InputFile component and the objects like StreamContent, IBrowserrFile makes it easy to upload file using Http Request from the Blazor WebAssembly application.  

Comments

Popular posts from this blog

Understanding Token Based Authentication in ASP.NET Core 3.1 using JSON WEB TOKENS

Using Model Binders in ASP.NET Core

Using Session State in ASP.NET Core