To upload a file, you can use the FileUpload control, which renders an <input type="file"> element. For a better user experience, you should also support dropping files from the file explorer and pasting images from the clipboard. The final result looks like this:
First, create a JavaScript file to register event handlers for drag-and-drop and clipboard interactions. The approach works by setting the inputElement.files property when a file is dropped or pasted from the clipboard, then raising the change event to notify Blazor that a file is ready. This lets Blazor handle all the file transfer complexity for you.
Let's create a new file named wwwroot/dropZone.js with the following content:
wwwroot/dropZone.js (JavaScript)
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 create the Razor component:
DropZone.razor (Razor)
@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:
DropZone.razor.css (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
Do you have a question or a suggestion about this post? Contact me!