Hands-On ASP.NET Core 3.1 : Learn MVC, Razor Pages, Web API, Entity Framework Core, and Blazor. Private online coaching for software developers. Click here to know more.

Upload files using InputFile component in Blazor

If you are tracking the development of ASP.NET Core 5, you are probably aware that RC1 of the framework is now available. One of the additions for Blazor Server and Blazor WebAssembly is the InputFile component to deal with file uploads. In this article I am going to take a quick look at the InputFile component and discuss a possible file upload process for Blazor Server as well as Blazor WebAssembly (ASP.NET Core hosted) apps.

Blazor Server

First we will use the InputFile component in a Blazor Server app. So, create a new Blazor Server app using Visual Studio.

Then add a new Razor Component called FileUpload.razor into the Pages folder.

Then add the following markup in the FileUpload.razor file.

<h1>Blazor Server File Upload</h1>

<h3>@Message</h3>

<form @onsubmit="OnSubmit">
    <InputFile OnChange="OnInputFileChange" multiple />
    <br /><br />
    <button type="submit">Upload Selected File(s)</button>
</form>

This markup is quite straightforward. It consists of a form that submits to OnSubmit() method. Inside, it houses an InoutFile component for uploading files. The InputFile component translates to a standard file input control in the browser. The presence of multiple attribute indicates that we can pick more than one files using the control. The OnChange event of InputFile component is raised when you select one or more files using the Browse button of the file input field. We will write OnInputFileChange() method to display how many files are picked by the user. The Submit button simply submits the form. The Message property is outputted at the top to display messages from the code discussed later.

The above markup will render a user interface similar to the one shown below: 

Now let's write some code that will do some work on the files selected by the user.

So, open FileUpload.razor file and add the following at the top of the file:

@page "/fileupload"
@using System.IO
@inject IWebHostEnvironment env

We configure a route for the FileUpload component we just created. Then we use System.IO namespace because we want to write / create files on the server. And then we also inject IWebHostEnvironment from Microsoft.AspNetCore.Hosting.namespace. We need the env object so that we can figure out the server's wwwroot path to save the uploaded files.

Next, add a @code block like this :

@code {
    string Message = "No file(s) selected";
    IReadOnlyList<IBrowserFile> selectedFiles;
}

As you can see, we create Message property with a default message. Later we will change this message as per the file selection. We also declare a list of IBrowserFile objects to hold the selected files.

Now add the OnInputFileChange() method in the @code block.

private void OnInputFileChange(InputFileChangeEventArgs e)
{
    selectedFiles = e.GetMultipleFiles();
    Message = $"{selectedFiles.Count} file(s) selected";
    this.StateHasChanged();
}

The OnInputFileChange() method receives InputFileChangeEventArgs as its parameter. Inside, we grab the files selected by the user using its GetMultipleFiles() method and store them into the selectedFile variable declared earlier. The Message is changed to reflect the number of files selected by the user.

The actual file upload happens in the OnSubmit() method as shown below:

private async void OnSubmit()
{
    foreach (var file in selectedFiles)
    {
        Stream stream = file.OpenReadStream();
        var path = $"{env.WebRootPath}\\{file.Name}";
        FileStream fs = File.Create(path);
        await stream.CopyToAsync(fs);
        stream.Close();
        fs.Close();
    }
    Message = $"{selectedFiles.Count} file(s) 
uploaded on server";
    this.StateHasChanged();
}

The above code iterates through the list of selected files. The OpenReadStream() method opens a stream through which the uploaded file content can be read. Remember that this is a Blazor Server app and your code is running on the server side. So, we combine the WebRootPath and Name property of IBrowserFile object to arrive at the final server side path for the file.

Since we want to save the uploaded file on the server, we create a new FileStream for the file. We then copy the file content to the FileStream using CopyToAsync() method. Finally, we close both the streams. Here, we are not doing any error handling or file size checking but in a more real-world situation you should consider adding that also.

Once all the files are uploaded on the server we display a success message to the user.

The following figure shows three image files uploaded to the wwwroot folder.

Blazor WebAssembly ASP.NET Core Hosted

Uploading and saving files in a Blazor Server app is relatively straightforward. Your code is running on the server and you can easily access the server's file system to save the uploaded files. However, a Blazor WebAssembly app can't do that directly because the code is running within the boundary of the browser. So, you need to do some additional work to pass the files from client side to the server and save them on the server.

Let's see how that can be accomplished.

Begin by creating a Blazor WebAssembly hosted app using Visual Studio. We need to create it as a hosted app because we will create a Web API to upload the selected files. 

Once created you will have three projects - Client, Server, and Shared.

Add a new class named UploadedFile in the Shared project.

public class UploadedFile
{
    public string FileName { get; set; }
    public byte[] FileContent { get; set; }
}

The UploadedFile consists of two properties - FileName, and FileContent. The FileName property will give the client side name of the file being uploaded and the FileContent byte array property will contain its content.

Then add a new API controller called FileUploadController in the Server project and write the following Post() action into it:

[Route("api/[controller]")]
[ApiController]
public class FileUploadController : ControllerBase
{

    private readonly IWebHostEnvironment env;

    public FileUploadController
(IWebHostEnvironment env)
    {
        this.env = env;
    }

    [HttpPost]
    public void Post(UploadedFile uploadedFile)
    {
        var path = $"{env.WebRootPath}\\{uploadedFile.FileName}";
        var fs = System.IO.File.Create(path);
        fs.Write(uploadedFile.FileContent, 0, 
uploadedFile.FileContent.Length);
        fs.Close();
    }
}

The code injects IWebHostEnvironment into the constructor. We need this object for deciding the server side path to save the uploaded files.

The Post() action receives UploadedFile object from your WebAssembly code (discussed later). Inside, we create a FileStream using File.Create() method. Then we write the byte[] to the file using Write() method. Finally we close the FileStream. As mentioned earlier, you can add some error handing and file size checking code here as per your requirement.

Next, add a new Razor Component called FileUpload.razor into the Client project's Pages folder.

<h1>Blazor WebAssembly File Upload</h1>

<h3>@Message</h3>

<form @onsubmit="OnSubmit">
    <InputFile OnChange="OnInputFileChange" multiple />
    <br /><br />
    <button type="submit">Upload Selected File(s)</button>
</form>

This is the same markup that you added for the Blazor Server example. Of course, the code is going to be different.

Add the following at the top of the FileUpload.razor file:

@page "/fileupload"
@inject HttpClient Http
@using System.IO

Here, we injected HttpClient into the FileUpload component. We will use HttpClient to call the Web API created earlier. We also use System.IO because we want to read the file's stream.

Next, add a @code block as shown below:

@code {
    string Message = "No file(s) selected";
    IReadOnlyList<IBrowserFile> selectedFiles;

    private void OnInputFileChange
(InputFileChangeEventArgs e)
    {
        selectedFiles = e.GetMultipleFiles();
        Message = $"{selectedFiles.Count} file(s) selected";
        this.StateHasChanged();
    }
}

This @code block consisting of Message, selectedFiles, and OnInputFileChange() method is similar to the Blazor Server example.

Then add OnSubmit() method as shown below:

private async void OnSubmit()
{
    foreach (var file in selectedFiles)
    {
        Stream stream = file.OpenReadStream();
        MemoryStream ms = new MemoryStream();
        await stream.CopyToAsync(ms);
        stream.Close();

        UploadedFile uploadedFile = new UploadedFile();
        uploadedFile.FileName = file.Name;
        uploadedFile.FileContent = ms.ToArray();
        ms.Close();

        await Http.PostAsJsonAsync<UploadedFile>
("/api/fileupload", uploadedFile);
    }
    Message = $"{selectedFiles.Count} file(s) uploaded on server";
    this.StateHasChanged();
}

This code iterates through the selectedFiles and opens a stream using OpenReadStream() method. This Stream is then copied into a MemoryStream object using CopyToAsync() method. Although I have used MemoryStream here for the sake of simplicity you could have also used Read() / ReadAsync() / ReadByte() methods of the Stream to read the file content.

Then a UploadedFile object is created and its FileName and FileContent properties are assigned. Notice how ToArray() method of MemoryStream is used to get the file content as a byte array.

Then the code makes a POST request to the FileUpload API controller by passing the UploadedFile object.

Once all the files are uploaded on the server,  a success message is displayed to the user. Remember that the files will be uploaded to the wwwroot folder of the Server project.

To know more about InputFile component go here.

That's it for now! Keep coding!!


Bipin Joshi is an independent software consultant, trainer, author, yoga mentor, and meditation teacher. He has been programming, meditating, and teaching for 24+ years. He conducts instructor-led online training courses in ASP.NET family of technologies for individuals and small groups. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get connected : Facebook  Twitter  LinkedIn  YouTube

Posted On : 28 September 2020


Tags : ASP.NET ASP.NET Core MVC .NET Framework C# Visual Studio


Subscribe to our newsletter

Get monthly email updates about new articles, tutorials, code samples, and how-tos getting added to our knowledge base.

  

Receive Weekly Updates