File Upload in ASP.NET Core

In one of the .NET Core trainings I was conducting recently, one of my students asked about uploading files using .NET Core application. Here's a solution. When we use ASP.NET MVC to upload a file, we use the HttpPostedFileBase class. This class is used to access the files uploaded by the client in an MVC application. In .NET Core, the IFromFile interface is used to represent a file that is sent with the HttpRequest. In this article we will use the IFromFile interface to upload the file. We will use Visual Studio 2017 and .NET Core 2.1. Information about the IFromFile interface can be read from this link.

Step 1: Open Visual Studio 2017 and create a new ASP.NET Core Web Application. Name this application as Core_FileUpload. Select the MVC project with .NET Core and ASP.NET Core 2.1 version for the project as shown in the following image:


mvc-app[8]


Step 2: In the project, add a new folder and name this folder as UploadedFiles. This folder will be used to store uploaded files. Step 3: In the Models folder, add a new class file. Name this file as FileManager.cs. In this file, add the following code

using Microsoft.AspNetCore.Http;
using System;
using System.IO;
using System.Threading.Tasks;

namespace Core_FileUpload.Models
{
public class FileManager
{
    public async Task UploadFile(IFormFile file)
    {
        try
        {
            bool isCopied = false;
            //1 check if the file length is greater than 0 bytes 
            if (file.Length > 0)
            {
                string fileName = file.FileName;
                //2 Get the extension of the file
                string extension = Path.GetExtension(fileName);
                //3 check the file extension as png
                if (extension == ".png" || extension == ".jpg")
                {
                    //4 set the path where file will be copied
                    string filePath = Path.GetFullPath(
                        Path.Combine(Directory.GetCurrentDirectory(), 
                                                    "UploadedFiles"));
                    //5 copy the file to the path
                    using (var fileStream = new FileStream(
                        Path.Combine(filePath, fileName), 
                                       FileMode.Create))
                    {
                        await file.CopyToAsync(fileStream);
                        isCopied = true;
                    }
                }
                else
                {
                    throw new Exception("File must be either .png or .JPG");
                }
            }
            return isCopied;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
}
}


The above code has following specifications. The UploadFile method accepts IFromFile interface. Note that the numbering below matches with the number in the comments applied on the code above.

  1. Check the file length if it is greater than 0. If the file size is greater than 0 then only the uploaded file will be further processed.
  2. Since we are restricting the file upload for .png and .jpg files, this step reads an extension of the file using Path.GetExtension() method. This method accepts the file name of which is read using FileName property of IFromFile interface.
  3. If the file extension is .png or .jpg, only  then will the file  used for uploading else the code will throw an exception.
  4. This code set the path for the UploadedFiles folder, to copy uploaded files in this folder.
  5. This code uploads the file in the UploadedFiles folder using CopyToAsync() method of the IFromFile interface.
Step 4: Since we will perform error handling in our application, lets change ErrorViewModel class from the Models class by adding the following properties:

public string ErrorMessage { get; set; }
public string Controller { get; set; }
public string Action { get; set; }
Step 5: Change the Error.cshtml file from the Shared sub-folder of Views folder as shown in the following markup:

<h2>@Model.ErrorMessage</h2>
<hr />
<div>
    <a asp-action="@Model.Action" asp-controller="@Model.Controller">
        Go Back
    </a>
</div>
Step 6: In the Controllers folder add a new empty MVC controller. Name this controller as FileUploadController. Add the following code in this controller:

using Core_FileUpload.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;
namespace Core_FileUpload.Controllers
{
    public class FileUploadController : Controller
    {
        FileManager fileManager;
        public FileUploadController()
        {
            fileManager = new FileManager();
        }
        public IActionResult Index()
        {
            return View("Index");
        }

        [HttpPost]
        public async  Task UploadFile(IFormFile file)
        {
            try
            {
                bool uploadSuccess = await fileManager.UploadFile(file);
                ViewBag.Message = "File Uploaded Successfully";
                return View("Index");
            }
            catch (Exception ex)
            {
                return View("Error", new ErrorViewModel() 
                                    {ErrorMessage= ex.Message });
            }
        }
    }
}

The controller class uses FileManager class created in Step 3. The Index action method of the controller class returns Index view. The UploadFile() action method accepts the IFromFile interface as a parameter and pass it to the UploadFile() method of the FileManager class. The UploadFile() method of the controller is executed for each HttpPost request. Step 7: Scaffold an Index view from the Index action method by right-clicking in the method and selecting Add View option. Select View template as empty view without model as shown in the following image:  index-view[7] 

Add the following markup in the Index.cshtml

@{
    ViewData["Title"] = "Index";
}

<h2>Uploading File in ASP.NET Core using 
    <strong>IFormFile</strong> Interface
</h2>
<form method="post" enctype="multipart/form-data" 
      asp-controller="FileUpload" asp-action="UploadFile">
    <div class="form-group">
        <div class="col-md-10">
            <p>Upload Image File (.png):</p>
            <input type="file" name="file"/>
        </div>
    </div>
    <hr />
    <div class="form-group">
        <div class="col-md-10">
            <input type="submit" value="Upload File" />
        </div>
    </div>
    <hr />
    <div class="form-group">
        <span>@ViewBag.Message</span>
    </div>
</form>
The above markup uses Html <form> tag. The form will be posted to UploadFile action method of the FileUpload controller. The enctype value multipart/form-data means the file will be send using POST request. Step 8: Modify the Configure() method of the StartUp.cs file for setting default route for FileUploadController as shown in the following code

app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=FileUpload}/{action=Index}/{id?}");
    });

Run the application, and it will display the following result:

 run-page[6]

Click on the Choose File button, this will open the File Open window. Select .png or .Jpg file from the windows. Once the file is selected, click on the Upload File button, the file will be uploaded in the UploadedFiles folder as shown in the following image: uploaded-files[3]

If you select a file other than .png or .jpg, an error page will be displayed as shown in the following image :

 error[4]

Conclusion: The IFromFile interface can be used to read and access uploaded files using HttpRequest in an ASP.NET Core application.




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: