Featured image of post How to Handle File Uploads with ASP.NET Core

How to Handle File Uploads with ASP.NET Core

Managing file uploads from end users is often required in web apps and APIs. This tutorial will guide you through the process of handling uploaded files using using ASP.NET Core and C#

NOTE

To follow this tutorial, you will need the following:

Overview

To allow files to be uploaded, you will:

  • Create ASP.NET Core Razor views that allow the user to select a file to upload
  • Create ASP.NET Core controllers to work with uploaded files

Of course, you will also want to do something with each uploaded file! In this tutorial, we’re going to write C# code to show information about the file, and also to scan it for malware using Verisys Antivirus API.

NOTE

Verisys Antivirus API is a language-agnostic REST API that allows you to easily add malware scanning to mobile apps, web apps and backend processing.

By scanning user-generated content and file uploads, Verisys Antivirus API can stop dangerous malware at the edge, before it reaches your servers, applications - or end users.

Project Setup

First, we need to create a new ASP.NET Core MVC project.

NOTE

Prefer the command line? You can create a new project simply by running:

1
dotnet new mvc -o Web
  1. Start Visual Studio and select File > New > Project

  2. Select the template named ASP.NET Core Web App (Model-View-Controller) and click Next

  1. Enter Web for the Project Name, choose a location to save the project, then click Next

  1. For the Framework, select .NET 8.0 (Long Term Support), then click Create

  1. The new project will be created, and you should see this folder structure in Visual Studio’s Solution Explorer:

Running the App

Now we’ve created a new project, let’s make sure it runs!

  1. Press CTRL+F5, or click the Start Without Debugging button:

  1. The first time you run the project, you will be asked to install an SSL/TLS certificate - click Yes on both popups:

  1. Visual Studio will then run the app, opening it in the default browser:

  1. While running your project, Visual Studio will open a terminal as well as the browser - this is for the default web server, Kestrel. If you press CTRL-C, it will shut down the web server and stop running your project.

File Upload

To enable file uploads, we need to add:

  1. View models, to encapsulate information about uploaded files
  2. A view with a form that allows users to select and upload a file
  3. A view to show information about the uploaded file
  4. A controller, to show the views and handle the uploaded file
  5. We’ll also update the main layout view, just to add links to our new pages

Let’s get started!

  1. First, create file Models/FileModels.cs, with content:
1
2
3
4
5
6
7
8
namespace Web.Models;

public record UploadModel(IFormFile File);
public record ResultModel(string Name, long Size, string MimeType, 
    MalwareStatus MalwareStatus, string[] Signals);

public record ApiResultModel(Guid Id, MalwareStatus Status, string[] Signals);
public enum MalwareStatus { Unknown, Clean, Threat }
  1. Next, create a folder Views/Upload, and inside a view file Views/Upload/Index.cshtml with content:
1
2
3
4
5
6
7
8
9
@model UploadModel
@{
    ViewData["Title"] = "Upload File";
}

<form method="post" enctype="multipart/form-data">
    <input type="file" asp-for="File">
    <button type="submit">Upload File</button>
</form>

Note the form’s encoding type (enctype) property is set to multipart/form-data - this is required for uploading files.

  1. Next, create view file Views/Upload/Result.cshtml with content:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@model ResultModel
@{
    ViewData["Title"] = "Result";
}

<h1>Result</h1>

<div>File Name: @Model.Name</div>
<div>File Size: @Model.Size</div>
<div>Mime Type: @Model.MimeType</div>
<div>Malware Status: @Model.MalwareStatus</div>
  1. Create a controller file Controllers/UploadController.cs with content:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
using Microsoft.AspNetCore.Mvc;
using Web.Models;

namespace Web.Controllers;

public class UploadController : Controller
{
    [HttpGet]
    public IActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public IActionResult Index(UploadModel model)
    {
        var result = new ResultModel(model.File.FileName, model.File.Length, model.File.ContentType, MalwareStatus.Unknown, []);
        return View("Result", result);
    }
}
  1. Finally, update the content of Views/Shared/_Layout.cshtml to add links to our new pages:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>@ViewData["Title"]</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand border-bottom mb-3">
            <div class="container-fluid">
                <div class="navbar-collapse">
                    <a class="nav-link text-dark" href="/upload">Upload File</a>
                    <a class="nav-link text-dark" href="/scan">Scan File</a>
                </div>
            </div>
        </nav>
    </header>

    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

</body>
</html>

It really is that simple!

Testing File Uploads

Now weโ€™re ready to test file uploads! ๐ŸŽ‰

  1. Begin by running the project again, either by pressing CTRL+F5, or by click the Start Without Debugging button

  2. Your browser should open automatically, showing a page that looks like this:

  1. Click the link Upload File, and you should see one of the new views we created:

  1. Press Choose File to select a file to upload, then and press the Upload File button

  2. If everything was set up correctly, you should see information about the file being shown in a web page:

Note that the Malware Status is shown as Unknown - we’ll fix that in the next section.

Malware Scanning

Now we’re going to use Verisys Antivirus API to scan uploaded files for malware. Following a similar pattern as for the previous section, we need to add:

  1. Registration of an HTTP client with the Dependency Injection (DI) container
  2. A view with a form that allows users to select and upload a file for scanning
  3. A view to show the results of the malware scan
  4. A controller, to show the views and send the uploaded file to Verisys Antivirus API

Let’s get started!

  1. In file Program.cs, replace line builder.Services.AddControllersWithViews(); with:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Register an HTTP client for use with Verisys Antivirus API
builder.Services.AddHttpClient<HomeController>(x =>
{
    x.BaseAddress = new Uri("https://eu1.api.av.ionxsolutions.com/v1/");
    x.DefaultRequestHeaders.Add("Accept", "application/json");
    x.DefaultRequestHeaders.Add("X-API-Key", "<YOUR API KEY HERE>");
});

builder.Services.AddControllersWithViews().AddJsonOptions(x => {
    x.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
    x.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
});
NOTE
The X-API-Key in the above code will need to be replaced with a real API key to scan files for real. Don’t have an API key? Subscribe now!
  1. Create a folder Views/Scan, and inside a view file Views/Scan/Index.cshtml with content:
1
2
3
4
5
6
7
8
9
@model UploadModel
@{
    ViewData["Title"] = "Scan File";
}

<form method="post" enctype="multipart/form-data">
    <input type="file" asp-for="File">
    <button type="submit">Scan File</button>
</form>
  1. Next, create view file Views/Scan/Result.cshtml with content:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@model ResultModel
@{
    ViewData["Title"] = "Result";
}

<h1>Result</h1>

<div>File Name: @Model.Name</div>
<div>File Size: @Model.Size</div>
<div>Mime Type: @Model.MimeType</div>
<div>Malware Status: @Model.MalwareStatus</div>

@if (Model.MalwareStatus == MalwareStatus.Threat)
{
    foreach (var signal in Model.Signals)
    {
        <div>Signal: @signal</div>
    }
}
  1. Finally, create a controller file Controllers/ScanController.cs with content:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System.Text.Json;
using Web.Models;

namespace Web.Controllers;

public class ScanController : Controller
{
    private readonly IHttpClientFactory clientFactory;
    private readonly JsonSerializerOptions jsonOptions;

    public ScanController(IHttpClientFactory clientFactory, IOptions<JsonOptions> jsonOptions)
    {
        this.clientFactory = clientFactory;
        this.jsonOptions = jsonOptions.Value.JsonSerializerOptions;
    }

    [HttpGet]
    public IActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> Index(UploadModel model)
    {
        using var client = this.clientFactory.CreateClient(nameof(HomeController));

        using var formData = new MultipartFormDataContent();
        formData.Add(new StreamContent(model.File.OpenReadStream()), "file", model.File.FileName);

        using var response = await client.PostAsync("malware/scan/file", formData);
        response.EnsureSuccessStatusCode();

        var apiModel = await response.Content.ReadFromJsonAsync<ApiResultModel>(this.jsonOptions);

        var result = new ResultModel(model.File.FileName, model.File.Length, model.File.ContentType, apiModel!.Status, apiModel.Signals);
        return View("Result", result);
    }
}

Testing Malware Scanning

Now weโ€™re ready to test malware scanning! ๐ŸŽ‰

  1. Begin by running the project again, either by pressing CTRL+F5, or by click the Start Without Debugging button

  2. Your browser should open automatically, showing a page that looks like this:

  1. Click the link Scan File, and you should see one of the new views we created:

  1. Press Choose File to select a file to upload, then and press the Upload File button

  2. If everything was set up correctly, you should see malware scan results from Verisys Antivirus API being shown in a web page: