File Upload with .NET Core and MVC - Part 1

Date Published: 5/6/2023

Check out my video courses at...

Pluralsight and at Udemy

File Upload with .NET Core and MVC - Part 1

Users frequently want the ability to upload files to a website. If you are using ASP.NET Core/8 with MVC and Bootstrap 5.x, you know that the normal file upload control does not look like the rest of your Bootstrapped controls. In this blog post you are going to learn how to modify the default look and feel with a couple of other looks to make the file upload control match the rest of the bootstrap styled HTML. In addition, you learn the very basics of uploading a file to a server.

Create a Simple Upload Page

Create a new project using Visual Studio 2022 using the ASP.NET Core Web App (Model-View-Controller) template. Name the new project UploadSample. You are going to create four different samples that show a progression from the simple HTML file upload control to a Bootstrap styled upload control using Font Awesome. Once you have the new project created, open the Views\Index.cshtml file and replace all of the code in this file with the following code.

@{
  ViewData["Title"] = "File Upload Samples";
}

<div class="mt-4 row">
  <div class="col text-center">
    <h1>File Upload Samples</h1>
  </div>
</div>

<div class="mt-4 row justify-content-center">
  <div class="list-group col-5">
    <a asp-action="Sample1"
       asp-controller="Home"
       class="list-group-item">
      Upload Control
    </a>
    <a asp-action="Sample2"
       asp-controller="Home"
       class="list-group-item">
       Style Upload Control Using Bootstrap
    </a>
    <a asp-action="Sample3"
       asp-controller="Home"
       class="list-group-item">
      Select a File
    </a>
    <a asp-action="Sample4"
       asp-controller="Home"
       class="list-group-item">
      Using an Icon
    </a>
    <a asp-action="Sample5"
       asp-controller="Home"
       class="list-group-item">
      Upload File to the Server
    </a>
  </div>
</div>

Listing 1: Create the index page to call the other samples you are going to create.

Open the Controllers\HomeController.cs file and add five action methods as shown below.

public IActionResult Sample1()
{
  return View();
}

public IActionResult Sample2()
{
  return View();
}

public IActionResult Sample3()
{
  return View();
}

public IActionResult Sample4()
{
  return View();
}

public IActionResult Sample5()
{
  return View();
}

Listing 2: Create the action methods to call each sample.

Right mouse-click on the Views\Home folder and add a new view named Sample1.cshtml. Replace the code in this file with the code shown in Listing 3. It is important to set the enctype attribute on the

element to be "multipart/form-data". This allows you to post the file information to the controller to upload.

@{
  ViewData["Title"] = "Upload Control";
}

<form method="post" enctype="multipart/form-data">
  <div class="mb-2">
    <label for="fileToUpload" class="form-label">
      Select File to Upload
    </label>
    <input id="fileToUpload" name="fileToUpload"
           type="file" class="form-control" />
  </div>
  <div class="mb-2">
    <button class="btn btn-primary">Upload</button>
  </div>
</form>

Listing 3: Use an <input type="file"> to create a file upload control.

Try it Out

Run the MVC application and click on the first link that reads "Upload Control".

You should see the normal HTML file upload control (Figure 1) created by setting the type="file" on the <input> tag you saw in Listing 3. There are a few problems with this control.

  1. The button part of the file input does not look like the other bootstrap buttons.
  2. You can't change the words on the button.
  3. You can't change the placeholder text within the input portion.
Figure 1: The file upload control does not look like it fits with the other Bootstrap controls.

Style Upload Control Using Bootstrap

As you are very limited on how you can style the <input type="file"> control, it is better to just hide this control and replace it with a <button> and <input type="text"> controls styled using Bootstrap as shown in Figure 2.

Figure 2: The file upload control can look just like any other bootstrap button.

The <input type="file"> must still be within the <form> because you need the functionality, but you can hide it. With Bootstrap 5 apply the class="d-none" to the file input to make it disappear. You are then free to add your own button and input controls and style them as you see fit. Right mouse-click on the Views\Home folder and add a new view named Sample2.cshtml. Replace the code in this file with the code shown in Listing 4.

@{
  ViewData["Title"] = "Style Upload Control Using Bootstrap";
}

<form method="post" enctype="multipart/form-data">
  <div class="mb-2">
    <label for="fileToUpload" class="form-label">
      Select File to Upload
    </label>
    <input id="fileToUpload" name="fileToUpload"
           type="file" class="d-none" />
    <div class="input-group">
      <button class="input-group-text btn btn-success">
        Select a File
      </button>
      <input id="fileDisplay" type="text"
             class="form-control" readonly="readonly" />
    </div>
  </div>
  <div class="mb-2 form-group">
    <button class="btn btn-primary">Upload</button>
  </div>
</form>

Listing 4: Hide the file input control and create a new "file control" using Bootstrap.

In the code shown in Listing 4 you added a new <div> with the class set to "input-group". Within this <div> you add a <button> with the class set to "input-group-text" and a normal text <input> that is read only. Using the input-group class puts the button and the input together so they look like a single control.

Try it Out

Run the MVC application and click on the link that reads "Style Upload Control Using Bootstrap". If you click on the control, it will not prompt the user to enter a file name like the original file control does. However, you can add that functionality on with just a little JavaScript.

Add the Select a File Functionality

Now that you have a better look and feel for your file control, you now need to add the ability for a user to choose a file from their local hard drive. Call a function you are going to write named "selectFileClick()" in response to the onclick event. And in response to the onclick event of the button within the input group, also call the "selectFileClick()" function. Right mouse-click on the Views\Home folder and add a new view named Sample3.cshtml. Replace the code in this file with the code shown in Listing 5.

Within the <script> tag at the bottom of the page, add the code shown below. The selectFileClick() function triggers the click event on the file upload control. The click event displays a file open dialog. If the user chooses a file, that file name is displayed in the read-only text box because of the 'change' event that you registered when the page was loaded on the <input type="file"> control.

@{
  ViewData["Title"] = "Select a File";
}

<form method="post" enctype="multipart/form-data">
  <div class="mb-2">
    <label for="fileToUpload" class="form-label">
      Select File to Upload
    </label>
    <input id="fileToUpload" name="fileToUpload"
           type="file" class="d-none" />
    <div class="input-group">
      <button class="input-group-text btn btn-success"
              type="button"
              onclick="selectFileClick();">
        Select a File
      </button>
      <input id="fileDisplay" type="text"
             class="form-control" readonly="readonly"
             onclick="selectFileClick();" />
    </div>
  </div>
  <div class="mb-2 form-group">
    <button class="btn btn-primary">Upload</button>
  </div>
</form>


@section Scripts {
  <script>
    $(document).on('change', ':file', function (e) {
      console.log('in change');
      $("#fileDisplay").val(e.target.value);
    });

    function selectFileClick() {
      console.log('in trigger');
      $("#fileToUpload").trigger("click");
    }
  </script>
}

Listing 5: Add the select a file functionality.

Try it Out

Run the MVC application and click on the link that reads "Select a File". If you now click on the control, it will prompt the user to enter a file name like the original file control does.

Using an Icon

Another look you might want to use for your file upload control is to add a nice icon to the end of the input text area. You can either do this with the button at the beginning of the input control as shown in Figure 3, or without it as shown in Figure 4.

Figure 3: Add an icon at the end of the text box that the user can also click upon.
Figure 4: Add an icon by itself and eliminate the button at the beginning of the control.

To add this icon, I used the Font Awesome (https://fontawesome.com/) library. Right mouse-click on the UploadSample project folder and choose Add | Client-Side Library... from the menu. In the pop-up form, choose 'cdnjs' as Provider and then type 'font-awesome' in the Library input text box as shown in Figure 5.

Figure 5: Install the Font Awesome library from within Visual Studio 2022.

Click the Install button to download and add the appropriate folders under the wwwroot folder in your project. Next, you need to open the Shared\_Layout.cshtml file and just after the <link> for Bootstrap, add the following link to reference the Font Awesome library.

<link rel="stylesheet" href="~/lib/font-awesome/css/all.css" />

After installing Font Awesome, add an <i> element and set the class attribute to "fa-regular fa-folder-open". There are many different icons you can display from Font Awesome, so definitely check out their website to see which icon suits you best.

Right mouse-click on the Views\Home folder and add a new view named Sample4.cshtml. Replace the code in this file with the code shown in Listing 6.

@{
  ViewData["Title"] = "Using an Icon";
}

<form method="post" enctype="multipart/form-data">
  <div class="mb-2">
    <label for="fileToUpload" class="form-label">
      Select File to Upload
    </label>
    <input id="fileToUpload" name="fileToUpload"
           type="file" class="d-none" />
    <div class="input-group">
      <button class="input-group-text btn btn-success"
              type="button"
              onclick="selectFileClick();">
        Select a File
      </button>
      <input id="fileDisplay" type="text"
             class="form-control" readonly="readonly"
             onclick="selectFileClick();" />
      <div class="input-group-text" onclick="selectFileClick();">
        <i class="fa-regular fa-folder-open"></i>
      </div>
    </div>
  </div>
  <div class="mb-2 form-group">
    <button class="btn btn-primary">Upload</button>
  </div>
</form>


@section Scripts {
  <script>
    $(document).on('change', ':file', function (e) {
      console.log('in change');
      $("#fileDisplay").val(e.target.value);
    });

    function selectFileClick() {
      console.log('in trigger');
      $("#fileToUpload").trigger("click");
    }
  </script>
}

Listing 6: Add a font awesome graphic to your file upload control.

Try it Out

Run the MVC application and click on the link that reads "Using an Icon". You should now see the Font Awesome icon appear at the end of the file input control.

Upload File to the Server

Now that you have seen how to style your file upload control, let's now get the file from the client and upload it to the server. Copy the Sample4.cshtml file to a new file named Sample5.cshtml. Open the Sample5.cshtml file and change the title.

@{
  ViewData["Title"] = "Upload File to Server";
}

Change the <form> to add an action attribute. The action attribute is set to the name of the POST method in the controller to call to perform the uploading of the data.

<form method="post" 
      action="UploadFile" 
      enctype="multipart/form-data">

Let's now fix up the HomeController class to hold data about the file, and to have this UploadFile() method. Open the Controllers\HomeController.cs file and add three public properties to the HomeController class to hold information about the file you are uploading.

public string FileNameOnServer { get; set; }
public long FileContentLength { get; set; }
public string FileContentType { get; set; }

Modify the constructor to initialize these variables.

public HomeController(ILogger<HomeController> logger)
{
  _logger = logger;
  FileNameOnServer = string.Empty;
  FileContentLength = 0;
  FileContentType = string.Empty;
}

Add a new method to the HomeController class named UploadFile. Write the code shown in Listing 7.

[HttpPost]
public IActionResult UploadFile(IFormFile fileToUpload)
{
  if (fileToUpload != null && fileToUpload.Length > 0) {
    // User selected a file
    // Get a temporary path
    FileNameOnServer = Path.GetTempPath();
    // Add the file name to the path
    FileNameOnServer += fileToUpload.FileName;
    // Get the file's length
    FileContentLength = fileToUpload.Length;
    // Get the file's type
    FileContentType = fileToUpload.ContentType;

    // Create a stream to write the file to
    using var stream = System.IO.File.Create(FileNameOnServer);
    // Upload file and copy to the stream
    fileToUpload.CopyTo(stream);

    // Return a success page
    return View("UploadComplete", this);
  }
  else {
    // User did not select a file
    return View("Index");
  }
}

Listing 7: Use the IFormFile as the class to post from the file input control on the HTML page.

The UploadFile() method is decorated with the [HttpPost] attribute because you are posting the file to upload to this method. The method receives this file object as the data type IFormFile. The concrete data type that is received is a FormFile object. This object has a few properties that you will find useful such as the length of the file, the file name, and the type of the file. These properties you are going to place into the properties you just created. Once you have the FormFile object you can now store it wherever you want on the server. In the code shown in Listing 7 you are going to create a new file and copy the file from the FormFile object into the new file located in your temporary directory. Once the file information is stored into the properties of the HomeController object and the file is copied, a page named UploadComplete is displayed to the user with the file information displayed.

Create an Upload Complete Page

Right mouse-click on the Views folder and create a new view named UploadComplete.cshtml. Replace all the code in this page to that shown in Listing 8.

The HomeController object is passed to the UploadComplete page, so you need an @model object as the first line in the page. The rest of the page is simply displays the file information you stored away in the public properties of the HomeController class.

@model UploadSample.Controllers.HomeController

@{
  ViewData["Title"] = "File Upload Success";
}

<div class="mt-4 row">
  <div class="col text-center">
    <h1>File Uploaded Successfully!</h1>

    <p>File Name: @Model.FileNameOnServer</p>
    <p>File Length: @Model.FileContentLength</p>
    <p>File Type: @Model.FileContentType</p>
  </div>
</div>

Listing 8: Create a page to provide the user with confirmation that you have received the file.

Asynchronous Uploading

Whenever you are performing any type of IO or other operation that might take some time, it is a good idea to make the method asynchronous. Modify the signature of the UploadFile() method (Listing 9) by adding the async keyword. You also want to return a Task<IActionResult> instead of just an IActionResult object. Modify the line of code that performs the copying of the file to call the CopyToAsync() instead of CopyTo(). Add the keyword await before this call as this is where this method may take some time.

[HttpPost]
public async Task<IActionResult> UploadFile(IFormFile fileToUpload)
{
  if (fileToUpload != null && fileToUpload.Length > 0) {
    // User selected a file
    // Get a temporary path
    FileNameOnServer = Path.GetTempPath();
    // Add the file name to the path
    FileNameOnServer += fileToUpload.FileName;
    // Get the file's length
    FileContentLength = fileToUpload.Length;
    // Get the file's type
    FileContentType = fileToUpload.ContentType;

    // Create a stream to write the file to
    using var stream = System.IO.File.Create(FileNameOnServer);
    // Upload file and copy to the stream
    await fileToUpload.CopyToAsync(stream);

    // Return a success page
    return View("UploadComplete", this);
  }
  else {
    // User did not select a file
    return View("Index");
  }
}

Listing 9: Make the file upload process asynchronous.

Summary

You do not have to settle for the default look and feel of the HTML file upload control. By simply turning off the style of the file upload control, you can style it in anyway you see fit. In this blog post you learned to use Bootstrap styles to make it look like a button. You also used an input group and used a text box and an icon from Font Awesome to make your control look very professional. A little JavaScript and jQuery and you have all the functionality of the normal HTML file upload control. Finally you created the code you need to upload the file and store the contents of the file on the server.


#fileupload #javascript #csharp #mvc #dotnetcore #pauldsheriff #development #programming

Check out my video courses at...

Pluralsight and at Udemy