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!!