File Upload using ASP.NET Core WEB API 3.1 and React.js Client Application

In this post we will see how to upload files (jpeg/png) to the ASP.NET Core 3.1 WEB API. We all know that WEB APIs are mainly used for Data Communication using JSON format across applications. But what if we want to use WEB APIs for accepting binary files from the client applications? 

In ASP.NET Core WEB API, the Request property of the type HttpRequest object from the ControllerBase class can be used to read the FormData posted by the client application. The following figure explains the approach of the application development


Figure 1: Uploading file from the Client Application to the ASP.NET Core WEB API

As seen in Figure 1, the WEB API project must be configured with Body Request limit to allow how much HTTP request body size will be accepted to process request. This is the FromData posted by the client application. The Static File middleware must be configured to define which folder will be used to store all uploaded files.

Creating ASP.NET Core WEB API

For WEB API development, I am using the latest Visual Studio 2019 and the ASP.NET Core 3.1 Web Application template which is used for WEB API Creation. .NET Core 3.1 SDK can be downloaded from this link

Step 1: Open Visual Studio 2019 and create a new ASP.NET Core 3.1 Web application. Name this application as FileUploadAPI.  Select the API template and ASP.NET Core 3.1 framework as shown in Figure 2.

Figure 2: Creating ASP.NET Core WEB API         

Step 2: Modify the ConfigureServices() method of the Startup class in the Startup.cs to configure FormOptions for MultiPart length in the posted Form body, this is required when the client posts the File from the Form Body.

Since we will be creating a React.js client application to post the file from a different port, we need to configure CORS policy.

Add the code as shown in Listing 1 in the ConfigureServices() method

 
public void ConfigureServices(IServiceCollection services)
{

 // adding the MultiPartBodyLength Configuration
 services.Configure(options => {
 // the max length of individual form values 
 options.ValueLengthLimit = int.MaxValue;
 // length of the each multipart body
 options.MultipartBodyLengthLimit = int.MaxValue;
 // this is used for buddering the form body into the memory
 options.MemoryBufferThreshold = int.MaxValue;
 });
// ends here

  services.AddControllers();
  // The cors policy definition
   services.AddCors(options => {
   options.AddPolicy("cors", policy=>{
    policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
     });
   });
 // ends here
 }
Listing 1: The Code in ConfigureServices() for the CORS policy and FormOptions  

Step 3: In the project, add a new folder and name it as UploadedFiles. To configure this folder in the StaticFile middleware, modify the code in the Configure() method of the Startup class in the Startup.cs file and customize the static file middleware along with the configuration of using the CORS policy as shown in Listing 2.

app.UseCors("cors");
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions()
{
 FileProvider = new PhysicalFileProvider
  (Path.Combine(Directory.GetCurrentDirectory(), @"UploadedFiles")),
  RequestPath = new PathString("/UploadedFiles")
 });
Listing 2: The Customization of Static file middleware and CORS policy in Configure method 

Step 4: In the Controllers folder, add a new empty API Controller and name it as FileUploadController.cs. In this controller, add the a new Action method of name UploadFile() with code as shown in the following listing:

using Microsoft.AspNetCore.Mvc;
using System;
using System.IO;
using System.Net.Http.Headers;

namespace FileUploadAPI.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class FileUploadController : ControllerBase
    {
        // the HTTP post request. The Body size limit is disabled 
        [HttpPost, DisableRequestSizeLimit]
        [ActionName("Upload")]
        public IActionResult UploadFile()
        {
            try
            {
                // 1. get the file form the request
                var postedFile = Request.Form.Files[0];
                // 2. set the file uploaded folder
                var uploadFolder = Path.Combine(Directory.GetCurrentDirectory(), "UploadedFiles");
                // 3. check for the file length, if it is more than 0 the save it
                if (postedFile.Length > 0)
                {
                    // 3a. read the file name of the received file
                    var fileName = ContentDispositionHeaderValue.Parse(postedFile.ContentDisposition)
                        .FileName.Trim('"');
                    // 3b. save the file on Path
                    var finalPath = Path.Combine(uploadFolder, fileName);
                    using (var fileStream = new FileStream(finalPath, FileMode.Create))
                    {
                        postedFile.CopyTo(fileStream);
                    }
                    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}");
            }
        }
    }
}

Listing 3: The File Uploading logic

The code in the UploadFile has following specification (Note: The following numbers match with the comments applied on the code)

  1. Read the posted body as form object from the HttpRequest object.
  2. Set the folder where uploaded files will be stored
  3. Check for the file length - if its more that 0 then process the file
    1. 3a. Read the file name of the posted file using the ContentDisposition property
    2. 3b. Write the file into the folder using FileStream object.
If the write operation is successful, then the success response will be sent, else an error will be returned. The Upload method contains DisableRequestSizeLinit attribute, this is used to disable the request body limit. This is useful for the large files to be posted to the WEB API. 

Note: Since while creating the API project, we kept the Configure for HTTP checkbox checked we will un-check HTTPS for this application. To do so,  go to the the project properties and un-check Enable SSL checkbox. 

So this is how we can create ASP.NET Core WEB API that will accept a file from the client and save it on the server. You can test this using clients like Postman, Advance REST Client.

Creating React.js Client application


In this section we will create a new React.js application. We will create this application using Visual Studio Code, which is a free IDE from Microsoft. This can be downloaded from this link. We also need Node.js. This can be downloaded from this link

Step 5: Open Node.js  command prompt and run the following command to install CLI from React.js application

npm install -g create-react-app

We can use this CLI to create a new React.js application.

Step 6: Create a new folder on the drive of name clientapp. Navigate to this folder from the command prompt and run the following command to create new React.js application

create-react-app my-react-app

This will create a new project with all dependencies. Open this folder in VSCode. Now we have the React.js application ready so we can add the logic to upload file by creating new component. 

Step 7: Since we will be making HTTP Post request from the React application, we require the axios package. To install this package, run the following command from the Node.js command prompt from the my-react-app path

npm install --save-dev axios

Step 8: In the src folder, add a new file and name it as FileUploadComponent.jsx. Add the following code in this file as shown in the following listing:

  
import React, { Component } from 'react';
import axios from 'axios';

class FileUploadComponent extends Component {
    constructor(props) {
        super(props);
        this.state = {
            selectedFile:'',
            status: '',
            progress:0
        }
    }
    selectFileHandler = (event)=>{
       //1. define the array for the file type e.g. png, jpeg
       const fileTypes = ['image/png', 'image/jpeg'];

       // 2. get the file type
        let file = event.target.files;
        console.log(`File ${file}`);
       // 3. the message for error if the file type of not matched
       let errMessage = [];
       // 4. to check the file type to match with the fileTypes array iterate 
       // through the types array
       if(fileTypes.every(extension=> file[0].type !=extension)){
           errMessage.push(`The file ${file.type} extension is not supported`);
       } else {
           this.setState({
               selectedFile: file[0]
           });
       }
    };
    // method contain logic to upload file
    uploadHandler = (event) => {
        // 1. the FormData object that contains the data to be posted to the 
        // WEB API
        const formData = new FormData();
        formData.append('file', this.state.selectedFile);
        
        // 2. post the file to the WEB API
        axios.post("http://localhost:38790/api/FileUpload/Upload", formData, {
      onUploadProgress: progressEvent => {
        this.setState({
          progress: (progressEvent.loaded / progressEvent.total*100)
        })
      }
    })
      .then((response) => { 
        this.setState({status:`upload success ${response.data}`});
      })
      .catch((error) => { 
        this.setState({status:`upload failed ${error}`});
      })
    }
    render() {
        return (
 <div>
              <h2>The File Upload DEMO</h2>
              <div>
                <label>Select File to Upload</label>
                <input type="file" onChange={this.selectFileHandler}/>
              </div>
              <hr/>
              <div>
              <button type="button"   onClick={this.uploadHandler}>Upload</button></div>
              <hr/>
              <div>{this.state.progress}</div>
              <br/>
              <div>{this.state.status}</div>  
            </div> 
);
    }
}

export default FileUploadComponent;

Listing 4: The File Upload Logic

The above code has methods for selecting file and uploading the file. The selectFileHandler() method defines and array of the file types which will be used for uploading e.g. jpg/png files. This method reads selected files using HTML input file element and then verifies the file type so that it can decide whether the file can be uploaded. If the selected file is of the type .jpg or .png then it will be assigned to the selectedFile state property.

The uploadHandler() method defines an instance of the FormData object. This is the object that carries the posted form values using key/value pair.

The selected file is appended into the FormData object and then it is posted to the WEB API using axios object and its post method. The onUploadProgressEvent is used to manage the file upload in stream.

Based on the status of upload operation, the status state variable will show the uploading status. The render() method contains the HTML where selectFileHandler()  method is bound to the onChange event of the input file element and the uploadHandler() method is bound to the onClick event of the button element.   

Step 9: Modify the index.js file by importing the FileUploadComponent in it and rendering the component as shown in the following listing

import FileUploadComponent from './FileUploadComponent';

ReactDOM.render(, document.getElementById('root'));


Listing 5: The FileUploadComponent rendering

Run the WEB API Project and to run the React.js client application. Run the following  command from the command prompt (the same folder path where the React.js application is created)

npm run start

The browser will show the result as shown in Figure 3:



Figure 3: The FileUploadComponent rendered

Click on choose file button, this will show the Open Dialog where you can select the jpg/png file as shown in figure 4.


Figure 4: The Open Dialog for choosing file 

Click on open. The file will be selected, to upload the file click on the Upload button, the file will be posted to the WEB API. Once the post is successful, the page will display a result as shown in Figure 5.


Figure 5: File Upload Success

To verify the upload, check the UploadedFiles folder in the WEB API project, it should show the newly uploaded file.

Conclusion: File Upload is a frequently required feature in modern applications. HttpRequest object in ASP.NET Core 3.1 provides an easy implementation of this feature. React.js (or any other JavaScript based application) can use HTTP communication objects (like axios) to post file to WEB API.





About The Author

Mahesh Sabnis is a Microsoft MVP having over 18 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). He also blogs regularly at DotNetCurry.com. Follow him on twitter @maheshdotnet

No comments: