Upload files with drag & drop or paste from clipboard in Blazor
To upload a file you can use the FileUpload
control. This control generates a <input type="file">
element and allows you to upload a file. To get a good user experience, you also need to support dropping files from the explorer or pasting images from the clipboard. The final result should look like this:
First, you need to create a JavaScript file to register some events to handle drag and drop events and clipboard events. The main idea is to set the inputElement.files
property when a file is dropped or pasted from the clipboard. Then, you can notify Blazor that the file is ready to be uploaded by raising the change
event. This way, you don't have to deal with file transfer by yourself. Indeed, Blazor already implements all the complexity required to upload files.
Let's create a new file named wwwroot/dropZone.js
with the following content:
export function initializeFileDropZone(dropZoneElement, inputFile) {
// Add a class when the user drags a file over the drop zone
function onDragHover(e) {
e.preventDefault();
dropZoneElement.classList.add("hover");
}
function onDragLeave(e) {
e.preventDefault();
dropZoneElement.classList.remove("hover");
}
// Handle the paste and drop events
function onDrop(e) {
e.preventDefault();
dropZoneElement.classList.remove("hover");
// Set the files property of the input element and raise the change event
inputFile.files = e.dataTransfer.files;
const event = new Event('change', { bubbles: true });
inputFile.dispatchEvent(event);
}
function onPaste(e) {
// Set the files property of the input element and raise the change event
inputFile.files = e.clipboardData.files;
const event = new Event('change', { bubbles: true });
inputFile.dispatchEvent(event);
}
// Register all events
dropZoneElement.addEventListener("dragenter", onDragHover);
dropZoneElement.addEventListener("dragover", onDragHover);
dropZoneElement.addEventListener("dragleave", onDragLeave);
dropZoneElement.addEventListener("drop", onDrop);
dropZoneElement.addEventListener('paste', onPaste);
// The returned object allows to unregister the events when the Blazor component is destroyed
return {
dispose: () => {
dropZoneElement.removeEventListener('dragenter', onDragHover);
dropZoneElement.removeEventListener('dragover', onDragHover);
dropZoneElement.removeEventListener('dragleave', onDragLeave);
dropZoneElement.removeEventListener("drop", onDrop);
dropZoneElement.removeEventListener('paste', onPaste);
}
}
}
Now you can use the create the razor component:
@inject IJSRuntime JSRuntime
@implements IAsyncDisposable
<h1>Upload files!</h1>
<div @ref="dropZoneElement" class="drop-zone">
<p>Drop a file or paste an image from the clipboard or select a file using the input</p>
<InputFile OnChange="@OnChange" @ref="inputFile" />
</div>
@* Display the uploaded image *@
<img src="@src" />
@code {
ElementReference dropZoneElement;
InputFile inputFile;
IJSObjectReference _module;
IJSObjectReference _dropZoneInstance;
string src;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// Load the JS file
_module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./dropZone.js");
// Initialize the drop zone
_dropZoneInstance = await _module.InvokeAsync<IJSObjectReference>("initializeFileDropZone", dropZoneElement, inputFile.Element);
}
}
// Called when a new file is uploaded
async Task OnChange(InputFileChangeEventArgs e)
{
using var stream = e.File.OpenReadStream();
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
src = "data:" + e.File.ContentType + ";base64," + Convert.ToBase64String(ms.ToArray());
}
// Unregister the drop zone events
public async ValueTask DisposeAsync()
{
if (_dropZoneInstance != null)
{
await _dropZoneInstance.InvokeVoidAsync("dispose");
await _dropZoneInstance.DisposeAsync();
}
if (_module != null)
{
await _module.DisposeAsync();
}
}
}
You can set the style in a file named DropZone.razor.css
:
.drop-zone {
padding: 20px;
width: 100%;
min-height: 100px;
border: 2px dashed #0087F7;
border-radius: 5px;
}
.drop-zone.hover {
border-style: solid;
}
#Additional resources
- Source code of this post (.NET 6)
- Source code of this post (.NET 5)
- ASP.NET Core Blazor file uploads
- Upload files and directories using an input, drag and drop, or copy and paste with HTML5
Do you have a question or a suggestion about this post? Contact me!