From 701b6c63b9bfa3ebce9fdbffeb8d8a6597c17d42 Mon Sep 17 00:00:00 2001 From: PriyankaKarthikeyan14 Date: Fri, 17 Oct 2025 15:29:34 +0530 Subject: [PATCH 1/2] Docs(BLAZ-982992): Revamp FileUpload documentation(file-upload/getting-started-with-maui-app.md, getting-started-with-server-app.md, getting-started-with-web-app.md, getting-started.md) --- .../getting-started-with-maui-app.md | 340 +++++++++----- .../getting-started-with-server-app.md | 341 +++++++++----- .../getting-started-with-web-app.md | 378 ++++++++++----- blazor/file-upload/getting-started.md | 440 +++++++++--------- .../images/blazor-fileupload-basic.gif | Bin 0 -> 62010 bytes .../images/blazor-fileupload-created.gif | Bin 0 -> 45947 bytes .../images/blazor-fileupload-fileselected.gif | Bin 0 -> 167216 bytes .../images/blazor-fileupload-memorystream.gif | Bin 0 -> 115805 bytes .../images/blazor-fileupload-valuechange.gif | Bin 0 -> 175137 bytes 9 files changed, 924 insertions(+), 575 deletions(-) create mode 100644 blazor/file-upload/images/blazor-fileupload-basic.gif create mode 100644 blazor/file-upload/images/blazor-fileupload-created.gif create mode 100644 blazor/file-upload/images/blazor-fileupload-fileselected.gif create mode 100644 blazor/file-upload/images/blazor-fileupload-memorystream.gif create mode 100644 blazor/file-upload/images/blazor-fileupload-valuechange.gif diff --git a/blazor/file-upload/getting-started-with-maui-app.md b/blazor/file-upload/getting-started-with-maui-app.md index 6662b07e35..1046e07260 100644 --- a/blazor/file-upload/getting-started-with-maui-app.md +++ b/blazor/file-upload/getting-started-with-maui-app.md @@ -3,13 +3,13 @@ layout: post title: Getting Started with FileUpload in Blazor MAUI App | Syncfusion description: Checkout and learn about the documentation for getting started with Blazor FileUpload Component in Blazor MAUI App. platform: Blazor -control: File Upload +control: FileUpload documentation: ug --- # Getting Started with Blazor File Upload Component -This guide describes how to integrate the Syncfusion® Blazor File Upload component into a .NET MAUI Blazor application. It covers setup using Visual Studio and Visual Studio Code, adding required packages, registering resources, and rendering the component. +This section guides you through the step-by-step process of integrating the Syncfusion® Blazor File Upload component into your Blazor MAUI application using both Visual Studio and Visual Studio Code. {% tabcontents %} @@ -17,21 +17,15 @@ This guide describes how to integrate the Syncfusion& ## Prerequisites -To use .NET MAUI project templates, install the Mobile development with .NET workload for Visual Studio. For details, refer to the .NET MAUI installation instructions or the Syncfusion Blazor extension documentation. - -- .NET MAUI installation: https://learn.microsoft.com/en-us/dotnet/MAUI/get-started/installation?tabs=vswin -- Syncfusion Blazor Extension: https://blazor.syncfusion.com/documentation/visual-studio-integration/template-studio +To use the MAUI project templates, install the Mobile development with the .NET extension for Visual Studio. For more details, refer to [here](https://learn.microsoft.com/en-us/dotnet/MAUI/get-started/installation?tabs=vswin) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-integration/template-studio). ## Create a new Blazor MAUI App in Visual Studio -Create a Blazor MAUI App using Visual Studio via [Microsoft Templates](https://learn.microsoft.com/en-us/dotnet/maui/get-started/first-app?pivots=devices-windows&view=net-maui-9.0&tabs=vswin). For detailed instructions, refer to [this Blazor MAUI App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/maui-blazor-app) documentation. +You can create a Blazor MAUI App using Visual Studio via [Microsoft Templates](https://learn.microsoft.com/en-us/dotnet/maui/get-started/first-app?pivots=devices-windows&view=net-maui-9.0&tabs=vswin). For detailed instructions, refer to [this Blazor MAUI App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/maui-blazor-app) documentation. ## Install Syncfusion® Blazor Inputs and Themes NuGet in the app -To use the Blazor File Upload component, install the following packages via the NuGet Package Manager (Tools → NuGet Package Manager → Manage NuGet Packages for Solution): - -- Syncfusion.Blazor.Inputs -- Syncfusion.Blazor.Themes +To add **Blazor File Upload** component in the app, open the NuGet package manager in Visual Studio (Tools → NuGet Package Manager → Manage NuGet Packages for Solution), search and install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/). {% tabs %} {% highlight C# tabtitle="Package Manager" %} @@ -50,18 +44,15 @@ N> Syncfusion® Blazor components are availa ## Prerequisites -Ensure the .NET MAUI workload is installed. Visual Studio Code uses the .NET CLI to create and manage projects. For details, see the .NET MAUI installation guide and the Syncfusion Blazor extension documentation for VS Code. - -- .NET MAUI installation: https://learn.microsoft.com/en-us/dotnet/maui/get-started/installation?view=net-maui-9.0&tabs=visual-studio-code -- Syncfusion Blazor Extension (VS Code): https://blazor.syncfusion.com/documentation/visual-studio-code-integration/create-project +To use the MAUI project templates, install the Mobile development with the .NET extension for Visual Studio Code. For more details, refer to [here](https://learn.microsoft.com/en-us/dotnet/maui/get-started/installation?view=net-maui-9.0&tabs=visual-studio-code) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-code-integration/create-project). ## Create a new Blazor MAUI App in Visual Studio Code -Create a Blazor MAUI App using Visual Studio Code via [Microsoft templates](https://learn.microsoft.com/en-us/dotnet/maui/get-started/first-app?pivots=devices-windows&view=net-maui-9.0&tabs=visual-studio-code) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-code-integration/create-project). For detailed instructions, refer to [this Blazor MAUI App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/maui-blazor-app) documentation. +You can create a Blazor MAUI App using Visual Studio Code via [Microsoft Templates](https://learn.microsoft.com/en-us/dotnet/maui/get-started/first-app?pivots=devices-windows&view=net-maui-9.0&tabs=visual-studio-code) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-code-integration/create-project). For detailed instructions, refer to [this Blazor MAUI App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/maui-blazor-app) documentation. -## Install Blazor Inputs and Themes NuGet in the App +## Install Syncfusion® Blazor Inputs and Themes NuGet in the App -To add **Blazor File Upload** component in the app, open the NuGet package manager in Visual Studio (Tools → NuGet Package Manager → Manage NuGet Packages for Solution), search and install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/). +To add **Blazor File Upload** component in the app, install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/) using the .NET CLI. {% tabs %} @@ -83,12 +74,12 @@ N> Syncfusion® Blazor components are availa ## Add Import Namespaces -Open **~/_Imports.razor** and import the `Syncfusion.Blazor` and `Syncfusion.Blazor.Inputs` namespaces. +Open the **~/_Imports.razor** file and import the `Syncfusion.Blazor` and `Syncfusion.Blazor.Inputs` namespace. {% tabs %} {% highlight razor tabtitle="~/_Imports.razor" %} -@using Syncfusion.Blazor +@using Syncfusion.Blazor @using Syncfusion.Blazor.Inputs {% endhighlight %} @@ -134,7 +125,7 @@ namespace MauiBlazorWindow; ## Add stylesheet and script resources -The theme stylesheet and scripts are provided via Static Web Assets from NuGet. Add the references to the head section of ~/wwwroot/index.html in the MAUI project. +The theme stylesheet and script can be accessed from NuGet through [Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets). Include the stylesheet and script references in the `` section of the **~/index.html** file. ```html @@ -142,16 +133,22 @@ The theme stylesheet and scripts are provided via Static Web Assets from NuGet. - //Blazor File Upload Component script reference. + ``` N> Check out the [Blazor Themes](https://blazor.syncfusion.com/documentation/appearance/themes) topic to discover various methods ([Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets), [CDN](https://blazor.syncfusion.com/documentation/appearance/themes#cdn-reference), and [CRG](https://blazor.syncfusion.com/documentation/common/custom-resource-generator)) for referencing themes in your Blazor application. Also, check out the [Adding Script Reference](https://blazor.syncfusion.com/documentation/common/adding-script-references) topic to learn different approaches for adding script references in your Blazor application. -## Add Blazor File Upload component +## Add Syncfusion® Blazor File Upload component + +The Syncfusion Blazor File Upload component allows you to seamlessly integrate file upload functionalities into your Blazor applications. It supports various features like asynchronous and synchronous uploads, file type validation, progress tracking, and custom templates. A common use case is enabling users to upload documents, images, or other files to a server, or process them directly within the client-side application. + +### Simple Code to render a Usable File Upload Component Add the Syncfusion® Blazor File Upload component in the **~/Pages/Index.razor** file. +The most basic way to render the File Upload component is by adding the `` tag to your `.razor` page. By default, this component provides a clean interface for users to select files locally. + {% tabs %} {% highlight razor %} @@ -162,51 +159,79 @@ Add the Syncfusion® Blazor File Upload comp ### How to Run the Sample on Windows -Run the sample in Windows Machine mode to host the .NET MAUI Blazor app on Windows. +Run the sample in Windows Machine mode, and it will run Blazor MAUI in Windows. ![Blazor FileUpload Component](./images/blazor-fileupload-maui-app.png) ### How to Run the Sample on Android -To run the Blazor File Upload component in an Android .NET MAUI app using the Android emulator, follow these steps: +To run the Blazor DataGrid in a Blazor Android MAUI application using the Android emulator, follow these steps: + +Refer [here](https://learn.microsoft.com/en-us/dotnet/maui/android/emulator/device-manager#android-device-manager-on-windows) to install and launch Android emulator. -- Install and launch the Android emulator: https://learn.microsoft.com/en-us/dotnet/maui/android/emulator/device-manager#android-device-manager-on-windows +N> If you encounter any errors while using the Android Emulator, refer to the following link for troubleshooting guidance[Troubleshooting Android Emulator](https://learn.microsoft.com/en-us/dotnet/maui/android/emulator/troubleshooting). -N> If issues occur with the Android Emulator, see Troubleshooting Android Emulator for guidance: https://learn.microsoft.com/en-us/dotnet/maui/android/emulator/troubleshooting. -![Blazor FileUpload Component](./images/blazor-fileupload-component.png) +* Press Ctrl+F5 (Windows) or +F5 (macOS) to launch the application. This will render the Syncfusion® Blazor File Upload component in your default web browser. -## Without server-side API endpoint +{% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" backgroundimage "[Blazor FileUpload Component](./images/blazor-fileupload-component.png)" %} -Upload the files and files of folders in the Blazor application without specifying the server-side API end point using [AsyncSettings](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderAsyncSettings.html). +#### Preview -### Save and Remove actions +* Press Ctrl+F5 (Windows) or +F5 (macOS) to launch the application. This will render the Syncfusion® Blazor File Upload component in your default web browser. -Get the uploaded files as file stream in the [ValueChange](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event argument. Now, you can write the save handler inside ValueChange event to save the files to desired location. Find the save action code below. +{% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" backgroundimage "[Blazor FileUpload Component](./images/blazor-fileupload-component.png)" %} + +![Blazor File Upload Basic Component](images/blazor-fileupload-basic.gif) + +## Use ValueChange Event + +The [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event fires when files are selected or removed from the uploader. This event is crucial for client-side processing of selected files, allowing you to access file details and their content, which is useful for previewing files or handling uploads without a server-side endpoint. + +### Code Example + +This example demonstrates how to use the [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event to save uploaded files directly to a local directory when the [`AutoUpload`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.SfUploader.html#Syncfusion_Blazor_Inputs_SfUploader_AutoUpload) property is set to `true`. This is useful for scenarios where you want to process files immediately after selection without an explicit upload button. {% tabs %} {% highlight razor %} @using Syncfusion.Blazor.Inputs +@using System.IO + + @code { private async Task OnChange(UploadChangeEventArgs args) { try { - foreach (var file in args.Files) + foreach (var fileEntry in args.Files) { - var path = @"" + file.FileInfo.Name; - FileStream filestream = new FileStream(path, FileMode.Create, FileAccess.Write); - await file.File.OpenReadStream(long.MaxValue).CopyToAsync(filestream); - filestream.Close(); + // Define a path where you want to save the file. + // For a Blazor Server app, this path will be on the server. + var uploadsFolder = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads"); + if (!Directory.Exists(uploadsFolder)) + { + Directory.CreateDirectory(uploadsFolder); + } + + // Construct the full file path. + var filePath = Path.Combine(uploadsFolder, fileEntry.FileInfo.Name); + + // Use a FileStream to write the uploaded file's content to the server. + await using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) + { + // OpenReadStream with long.MaxValue allows reading the entire stream. + await fileEntry.File.OpenReadStream(long.MaxValue).CopyToAsync(fileStream); + } + Console.WriteLine($"File '{fileEntry.FileInfo.Name}' saved successfully to '{filePath}'"); } } catch (Exception ex) { - Console.WriteLine(ex.Message); + Console.WriteLine($"Error saving file: {ex.Message}"); } } } @@ -214,20 +239,73 @@ Get the uploaded files as file stream in the [ValueChange](https://help.syncfusi {% endhighlight %} {% endtabs %} -![Blazor FileUpload displays Updated Files](./images/blazor-fileupload-with-updated-files.png) +N> When saving files directly in a Blazor Server application using [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) and [`AutoUpload`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.SfUploader.html#Syncfusion_Blazor_Inputs_SfUploader_AutoUpload), the files are saved on the server where the Blazor Server app is running, not on the client's machine. You need appropriate file system permissions for the server process to write to the specified directory. Also, ensure the target directory (`wwwroot/uploads` in this example) exists or is created programmatically. In a production environment, consider secure storage solutions for uploaded files. + +### Preview + +{% previewsample "https://blazorplayground.syncfusion.com/embed/hDVyZkrqBvaSlvht?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -While clicking on the remove icon in the file list, you will get the [OnRemove](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnRemove) event with removing file name as argument. So, you can write the remove handler inside OnRemove event to remove the particular file from desired location. Find the remove action code below. +![Blazor File Upload ValueChange Event](images/blazor-fileupload-valuechange.gif) + + +## Memory stream + +When you need to process uploaded files in memory—perhaps for resizing images, reading content, or sending them to another service without saving them to disk first—using a `MemoryStream` is an efficient approach. This is particularly useful for temporary processing or when dealing with sensitive data that shouldn't persist on the file system. + +### Code Example + +This example demonstrates how to read the content of an uploaded file into a [MemoryStream Class](https://learn.microsoft.com/en-us/dotnet/api/system.io.memorystream)`. This allows you to perform in-memory operations on the file, such as converting an image to a Base64 string, without requiring disk I/O. {% tabs %} -{% highlight cshtml %} +{% highlight razor %} -Private void onRemove(RemovingEventArgs args) +@using Syncfusion.Blazor.Inputs +@using System.IO + + + + + +@if (!string.IsNullOrEmpty(base64Image)) { - foreach(var removeFile in args.FilesData) +

Uploaded Image Preview (Base64)

+ +} + +@code { + private string base64Image; + + private async Task OnValueChangeMemoryStream(UploadChangeEventArgs args) { - if (File.Exists(Path.Combine(@"rootPath", removeFile.Name))) + base64Image = string.Empty; // Clear previous image + + foreach (var fileEntry in args.Files) { - File.Delete(Path.Combine(@"rootPath", removeFile.Name)) + if (fileEntry.FileInfo.Type.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) + { + // Create a MemoryStream to hold the file content. + using var memoryStream = new MemoryStream(); + + // Copy the file's content from the upload stream to the MemoryStream. + await fileEntry.File.OpenReadStream(long.MaxValue).CopyToAsync(memoryStream); + + // Convert the MemoryStream content to a byte array. + byte[] imageBytes = memoryStream.ToArray(); + + // Convert byte array to Base64 string for display or further processing. + base64Image = Convert.ToBase64String(imageBytes); + Console.WriteLine($"Image '{fileEntry.FileInfo.Name}' loaded into MemoryStream and converted to Base64."); + } + else + { + Console.WriteLine($"File '{fileEntry.FileInfo.Name}' is not an image and won't be processed for Base64 preview."); + // For non-image files, you could read their content as text or handle differently. + // Example for text file: + // memoryStream.Position = 0; // Reset stream position + // using var reader = new StreamReader(memoryStream); + // var content = await reader.ReadToEndAsync(); + // Console.WriteLine($"Content of {fileEntry.FileInfo.Name}: {content}"); + } } } } @@ -235,114 +313,152 @@ Private void onRemove(RemovingEventArgs args) {% endhighlight %} {% endtabs %} -## With server-side API endpoint +N> When using `MemoryStream` for large files, be mindful of server memory consumption. If you expect very large files, consider processing them in chunks or saving them to temporary storage before processing to avoid out-of-memory exceptions. The `long.MaxValue` in `OpenReadStream` indicates the maximum buffer size. In a Blazor Server app, `Stream` operations occur on the server. + +#### Preview + +{% previewsample "https://blazorplayground.syncfusion.com/embed/BjreNaLUhdwxzvHS?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -The upload process can also be integrated with server endpoints via SaveUrl and RemoveUrl to manage files on the server. +#### Gif -N> * The save action is required to handle file uploads on the server. -
* The remove action is optional and handles deleting files on the server. +![Blazor File Upload Memory Stream Example](images/blazor-fileupload-memorystream.gif) -The save action handler upload the files that needs to be specified in the [SaveUrl](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderAsyncSettings.html#Syncfusion_Blazor_Inputs_UploaderAsyncSettings_SaveUrl) property. -The save handler receives the submitted files and manages the save process in server. After uploading the files to server location, the color of the selected file name changes to green and the remove icon is changed as bin icon. +## Created Event -The remove action is optional. The remove action handler removes the files that needs to be specified in the [RemoveUrl](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderAsyncSettings.html#Syncfusion_Blazor_Inputs_UploaderAsyncSettings_RemoveUrl) property. [OnActionComplete](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnActionComplete) event triggers after all the selected files have been processed to upload successfully or failed to server. +The [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event fires after the File Upload component has been rendered and initialized. This event is a good place to perform any initial setup, attach custom event listeners to the component's DOM elements, or apply custom styling that requires the component to be fully rendered. + +### Code Example + +This example shows how to use the [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event to add a custom message or dynamically change some aspect of the uploader's appearance right after it's created. {% tabs %} -{% highlight cshtml %} +{% highlight razor %} -[Route("api/[controller]")] -public class SampleDataController : Controller -{ - public string uploads = ".\\Uploaded Files"; // replace with your directory path +@using Syncfusion.Blazor.Inputs - [HttpPost("[action]")] - public async Task Save(IFormFile UploadFiles) // Save the uploaded file here - { - if (UploadFiles.Length > 0) - { - //Create directory if not exists - if (!Directory.Exists(uploads)) - { - Directory.CreateDirectory(uploads); - } + + + - var filePath = Path.Combine(uploads, UploadFiles.FileName); - if (System.IO.File.Exists(filePath)) - { - //Return conflict status code - return new StatusCodeResult(StatusCodes.Status409Conflict); - } - using (var fileStream = new FileStream(filePath, FileMode.Create)) - { - //Save the uploaded file to server - await UploadFiles.CopyToAsync(fileStream); - } - } - return Ok(); - } +

@statusMessage

+@code { + private string statusMessage = "Uploader not yet created."; - [HttpPost("[action]")] - public void Remove(string UploadFiles) // Delete the uploaded file here + private void OnUploaderCreated() { - if(UploadFiles != null) - { - var filePath = Path.Combine(uploads, UploadFiles); - if (System.IO.File.Exists(filePath)) - { - //Delete the file from server - System.IO.File.Delete(filePath); - } - } + statusMessage = "Syncfusion File Uploader has been successfully created and initialized!"; + Console.WriteLine(statusMessage); + // You could also interact with JavaScript to modify DOM here if needed. + // For example: JSRuntime.InvokeVoidAsync("someJsFunction"); } } {% endhighlight %} {% endtabs %} -The [OnFailure](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnFailure) event is triggered when there is a failure in the AJAX request during the uploading or removing of files. It provides a way to handle and respond to any errors or issues that occur during the file upload or removal process. +N> The [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event is useful for client-side JavaScript interop if you need to manipulate the DOM elements of the uploader component immediately after it's ready. However, for most Blazor-specific customizations (like custom templates), you should use the built-in Blazor features. + +### Preview + +{% previewsample "https://blazorplayground.syncfusion.com/embed/VtLyNuVUBGtPZrdo?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} + +![Blazor File Upload Created Example](images/blazor-fileupload-created.gif) + + +## File Selected Event + +The [`FileSelected Event`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_FileSelected)event is triggered when files are chosen from the file explorer dialog, but **before** the [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event. This event provides an opportunity to perform validations on the selected files (e.g., file size, type, count) and decide whether to proceed with the upload/value change or cancel the selection. It's ideal for immediate client-side feedback or preventative actions. + +### Code Example + +This example demonstrates how to use the [FileSelected Event](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_FileSelected) event to prevent files larger than a certain size. {% tabs %} {% highlight razor %} - - - +@using Syncfusion.Blazor.Inputs +@using System.Linq + + + +

@validationMessage

+ @code { - private void OnFailureHandler(FailureEventArgs args) - { - // Here, you can customize your code. - } - private void OnActionCompleteHandler(ActionCompleteEventArgs args) + private string validationMessage = ""; + private readonly long MaxFileSize = 1024 * 1024; // 1 MB + + private void OnFileSelected(SelectedEventArgs args) { - // Here, you can customize your code. + validationMessage = ""; + foreach (var file in args.FilesData) + { + if (file.Size > MaxFileSize) + { + validationMessage += $"Error: File '{file.Name}' exceeds {MaxFileSize / (1024 * 1024)} MB limit. "; + args.Cancel = true; // Prevents this file from being added + } + } + if (!string.IsNullOrEmpty(validationMessage)) + { + Console.WriteLine(validationMessage); + } + else + { + Console.WriteLine("Files selected successfully and passed initial validation."); + } } } {% endhighlight %} {% endtabs %} -## Configure allowed file types +N> Setting `args.Cancel = true` in the `FileSelected` event will prevent the file (or files if `args.Files` contains multiple) from being added to the uploader's internal file list. This is a client-side validation and should be complemented with server-side validation for robust security and data integrity. + +### Preview + +{% previewsample "https://blazorplayground.syncfusion.com/embed/BDLIZuBUVwEJoJpz?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} + +![Blazor File Upload File Selected Example](images/blazor-fileupload-fileselected.gif) + -Allow the specific files alone to upload using the [AllowedExtensions](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderModel.html#Syncfusion_Blazor_Inputs_UploaderModel_AllowedExtensions) property. The extension can be represented as collection by comma separators. The uploader component filters the selected or dropped files to match against the specified file types and processes the upload operation. The validation happens when you specify value to inline attribute to accept the original input element. +## OnFileListRender + +The [`OnFileListRender`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnFileListRender) event allows you to customize individual file list items before they are rendered in the uploader's UI. This is highly useful for scenarios where you need to display additional information alongside each file, such as a custom preview, metadata, or actions. + +### Code Example + +This example demonstrates how to use [`OnFileListRender`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnFileListRender) {% tabs %} {% highlight razor %} - +@using Syncfusion.Blazor.Inputs + + + + + + +@code { + SfUploader fileobj; + private void OnFileListRenderHandler(FileListRenderingEventArgs args) + { + + } +} {% endhighlight %} {% endtabs %} -{% previewsample "https://blazorplayground.syncfusion.com/embed/rXhzDsrOqbKVNviI?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" backgroundimage "[Allowing Specific Files in Blazor FileUpload](./images/blazor-fileupload-allow-specific-file.png)" %} - -N> View the sample on GitHub for a complete getting-started example: https://github.com/SyncfusionExamples/Blazor-Getting-Started-Examples/tree/main/FileUpload. +N> [View Sample in GitHub](https://github.com/SyncfusionExamples/Blazor-Getting-Started-Examples/tree/main/FileUpload). ## See also -1. [Getting started with Syncfusion® Blazor for client-side in .NET Core CLI](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-dotnet-cli) -2. [Getting started with Syncfusion® Blazor for client-side in Visual Studio](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-visual-studio) -3. [Getting started with Syncfusion® Blazor for server-side in .NET Core CLI](https://blazor.syncfusion.com/documentation/getting-started/blazor-server-side-dotnet-cli) -4. [Getting started with Syncfusion® File Upload in Blazor WebAssembly using Visual Studio](https://blazor.syncfusion.com/documentation/file-upload/how-to/getting-started-with-blazor-webassembly) \ No newline at end of file +1. [Getting Started with Syncfusion® Blazor for client-side in .NET Core CLI](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-dotnet-cli) +2. [Getting Started with Syncfusion® Blazor for client-side in Visual Studio](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-visual-studio) +3. [Getting Started with Syncfusion® Blazor for server-side in .NET Core CLI](https://blazor.syncfusion.com/documentation/getting-started/blazor-server-side-dotnet-cli) +4. [Getting Started with Syncfusion® File Upload in Blazor WebAssembly using Visual Studio](https://blazor.syncfusion.com/documentation/file-upload/how-to/getting-started-with-blazor-webassembly) +5. [How to convert images to Base64 string with Blazor File Upload](https://support.syncfusion.com/kb/article/21178/how-to-convert-images-to-base64-string-with-blazor-file-upload) \ No newline at end of file diff --git a/blazor/file-upload/getting-started-with-server-app.md b/blazor/file-upload/getting-started-with-server-app.md index cc2988b435..fc321f86b7 100644 --- a/blazor/file-upload/getting-started-with-server-app.md +++ b/blazor/file-upload/getting-started-with-server-app.md @@ -3,15 +3,15 @@ layout: post title: Getting Started with Blazor FileUpload Component | Syncfusion description: Checkout and learn about getting started with Blazor FileUpload component in Blazor Server Application. platform: Blazor -control: File Upload +control: FileUpload documentation: ug --- -# Getting Started with Blazor File Upload Component in Server App +# Getting Started with Blazor File Upload Component -This section explains how to add the [Blazor File Upload](https://www.syncfusion.com/blazor-components/blazor-file-upload) component to a Blazor Server app using Visual Studio, Visual Studio Code, or the .NET CLI. +This section briefly explains how to include the [Blazor File Upload](https://www.syncfusion.com/blazor-components/blazor-tree-grid) component in your Blazor Server Application using Visual Studio, Visual Studio Code, and the .NET CLI. -To get started quickly with the Blazor File Upload component, review the sample in this [GitHub repository](https://github.com/SyncfusionExamples/Blazor-Getting-Started-Examples/tree/main/FileUpload). +To get started quickly with the Blazor File Upload, you can check out this [GitHub sample](https://github.com/SyncfusionExamples/Blazor-Getting-Started-Examples/tree/main/FileUpload). {% tabcontents %} @@ -19,15 +19,15 @@ To get started quickly with the Blazor File Upload component, review the sample ## Prerequisites -* Review the [system requirements for Blazor components](https://blazor.syncfusion.com/documentation/system-requirements) to ensure the development environment is configured correctly. +* [System requirements for Blazor components](https://blazor.syncfusion.com/documentation/system-requirements) ## Create a new Blazor App in Visual Studio -Create a **Blazor Server App** using **Blazor Web App** template in Visual Studio via [Microsoft templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-7.0&pivots=vs) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-integration/template-studio). For detailed instructions, refer to [this Blazor Server App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/blazor-server-side-visual-studio) documentation. +You can create a **Blazor Server App** using the **Blazor Web App** template in Visual Studio via [Microsoft Templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-7.0&pivots=vs) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-integration/template-studio). For detailed instructions, refer to [this Blazor Server App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/blazor-server-side-visual-studio) documentation. ## Install Syncfusion® Blazor Inputs and Themes NuGet in the App -To add the **Blazor File Upload** component, open NuGet Package Manager in Visual Studio (Tools → NuGet Package Manager → Manage NuGet Packages for Solution), then search for and install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/). Alternatively, use the following Package Manager commands. +To add the **Blazor File Upload** component in the app, open the NuGet package manager in Visual Studio (*Tools → NuGet Package Manager → Manage NuGet Packages for Solution*), search and install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/). Alternatively, you can utilize the following package manager command to achieve the same. {% tabs %} {% highlight C# tabtitle="Package Manager" %} @@ -38,7 +38,7 @@ Install-Package Syncfusion.Blazor.Themes -Version {{ site.releaseversion }} {% endhighlight %} {% endtabs %} -N> Syncfusion® Blazor components are available on [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). For a full list of packages and component details, see the [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic. +N> Syncfusion® Blazor components are available in [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). Refer to [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for available NuGet packages list with component details. {% endtabcontent %} @@ -46,13 +46,13 @@ N> Syncfusion® Blazor components are availa ## Prerequisites -* Review the [system requirements for Blazor components](https://blazor.syncfusion.com/documentation/system-requirements) and ensure the .NET SDK is installed. +* [System requirements for Blazor components](https://blazor.syncfusion.com/documentation/system-requirements) ## Create a new Blazor App in Visual Studio Code -Create a **Blazor Server App** using Visual Studio Code via [Microsoft Templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-7.0&pivots=vsc) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-code-integration/create-project). For detailed instructions, refer to [this Blazor Server App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/blazor-server-side-visual-studio?tabcontent=visual-studio-code) documentation. +You can create a **Blazor Server App** using Visual Studio Code via [Microsoft Templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-7.0&pivots=vsc) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-code-integration/create-project). For detailed instructions, refer to [this Blazor Server App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/blazor-server-side-visual-studio?tabcontent=visual-studio-code) documentation. -Alternatively, you can create a server application using the following command in the terminal (Ctrl+`). Depending on the SDK version, commands may vary between classic Blazor Server and Blazor Web App templates. +Alternatively, you can create a server application using the following command in the terminal(Ctrl+`). {% tabs %} @@ -67,9 +67,9 @@ cd BlazorApp ## Install Syncfusion® Blazor Inputs and Themes NuGet in the App -* Press Ctrl+` to open the integrated terminal in Visual Studio Code. -* Ensure you’re in the project root directory where your `.csproj` file is located. -* Run the following commands to install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/) and restore dependencies. +* Press Ctrl+` to open the integrated terminal in Visual Studio Code. +* Ensure you’re in the project root directory where your `.csproj` file is located. +* Run the following command to install a [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/) NuGet package and ensure all dependencies are installed. {% tabs %} @@ -83,7 +83,7 @@ dotnet restore {% endtabs %} -N> Syncfusion® Blazor components are available on [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). Refer to the [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for the available packages and component details. +N> Syncfusion® Blazor components are available in [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). Refer to [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for available NuGet packages list with component details. {% endtabcontent %} @@ -91,7 +91,7 @@ N> Syncfusion® Blazor components are availa ## Prerequisites -Install the latest [.NET SDK](https://dotnet.microsoft.com/en-us/download). If previously installed, verify the version with the following command. +Latest version of the [.NET Core SDK](https://dotnet.microsoft.com/en-us/download). If you previously installed the SDK, you can determine the installed version by executing the following command in a command prompt (Windows) or terminal (macOS) or command shell (Linux). {% tabs %} {% highlight c# tabtitle=".NET CLI" %} @@ -103,7 +103,7 @@ dotnet --version ## Create a Blazor Server App using .NET CLI -Run the `dotnet new blazorserver` command to create a new Blazor Server app in a command prompt (Windows), terminal (macOS), or shell (Linux). Depending on the SDK version, Blazor Web App templates can also be used with server interactivity. +Run the `dotnet new blazorserver` command to create a new Blazor Server application in a command prompt (Windows) or terminal (macOS) or command shell (Linux). {% tabs %} {% highlight c# tabtitle=".NET CLI" %} @@ -114,11 +114,11 @@ cd BlazorApp {% endhighlight %} {% endtabs %} -This command creates a new Blazor Server app in a directory named `BlazorApp` at your current location. See the [Create Blazor app](https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/create) and [dotnet new](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-new) topics for more details. +This command creates a new Blazor app project and places it in a new directory called `BlazorApp` inside your current location. See [Create Blazor app topic](https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/create) and [dotnet new CLI command](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-new) topics for more details. ## Install Syncfusion® Blazor Inputs and Themes NuGet in the App -Use the .NET CLI to add the **Blazor File Upload** component packages. Run the following commands in a command prompt (Windows) or terminal (Linux and macOS) to install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs/) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/). For more information, see [Install and manage packages using the dotnet CLI](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-dotnet-cli). +Here's an example of how to add the **Blazor File Upload** component in the application using the following command in the command prompt (Windows) or terminal (Linux and macOS) to install a [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs/) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/) NuGet package. See [Install and manage packages using the dotnet CLI](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-dotnet-cli) topics for more details. {% tabs %} {% highlight c# tabtitle=".NET CLI" %} @@ -130,7 +130,7 @@ dotnet restore {% endhighlight %} {% endtabs %} -N> Syncfusion® Blazor components are available on [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). Refer to the [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for the available packages and component details. +N> Syncfusion® Blazor components are available in [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). Refer to [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for available NuGet packages list with component details. {% endtabcontent %} @@ -149,7 +149,7 @@ Open the **~/_Imports.razor** file and import the `Syncfusion.Blazor` and `Syncf ## Register Syncfusion® Blazor Service -Register the Syncfusion® Blazor service in the **~/Program.cs** file of your Blazor Server app. +Register the Syncfusion® Blazor Service in the **~/Program.cs** file of your Blazor Server App. {% tabs %} {% highlight C# tabtitle="~/Program.cs" hl_lines="3 10" %} @@ -172,7 +172,7 @@ builder.Services.AddSyncfusionBlazor(); ## Add stylesheet and script resources -The theme stylesheet and script are available via [Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets). Include the stylesheet in the `` and the script reference at the end of the `` in the **App.razor** file as shown below. +The theme stylesheet and script can be accessed from NuGet through [Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets). Include the stylesheet and script in the `` and the script reference at the end of the `` in the **App.razor** file as shown below: ```html @@ -186,17 +186,23 @@ The theme stylesheet and script are available via [Static Web Assets](https://bl //Blazor File Upload Component script reference. - + ``` -N> See the [Blazor Themes](https://blazor.syncfusion.com/documentation/appearance/themes) topic for options to reference themes ([Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets), [CDN](https://blazor.syncfusion.com/documentation/appearance/themes#cdn-reference), and [CRG](https://blazor.syncfusion.com/documentation/common/custom-resource-generator)). For adding scripts, refer to [Adding Script Reference](https://blazor.syncfusion.com/documentation/common/adding-script-references). +N> Check out the [Blazor Themes](https://blazor.syncfusion.com/documentation/appearance/themes) topic to discover various methods ([Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets), [CDN](https://blazor.syncfusion.com/documentation/appearance/themes#cdn-reference), and [CRG](https://blazor.syncfusion.com/documentation/common/custom-resource-generator)) for referencing themes in your Blazor application. Also, check out the [Adding Script Reference](https://blazor.syncfusion.com/documentation/common/adding-script-references) topic to learn different approaches for adding script references in your Blazor application. + +## Add Syncfusion® Blazor File Upload component + +The Syncfusion Blazor File Upload component allows you to seamlessly integrate file upload functionalities into your Blazor applications. It supports various features like asynchronous and synchronous uploads, file type validation, progress tracking, and custom templates. A common use case is enabling users to upload documents, images, or other files to a server, or process them directly within the client-side application. -## Add Blazor File Upload component +### Simple Code to render a Usable File Upload Component -Add the Syncfusion® Blazor File Upload component in the **~/Components/Pages/Home.razor** file. If the interactivity location is set to `Per page/component`, define a render mode at the top of the `Home.razor` page. +The most basic way to render the File Upload component is by adding the `` tag to your `.razor` page. By default, this component provides a clean interface for users to select files locally. -N> If the interactivity location is set to `Global` and the **Render Mode** is `Server`, the render mode is configured in the `App.razor` file by default. +Add the Syncfusion® Blazor File Upload component in the **~/Components/Pages/Home.razor** file. If an interactivity location as `per page/component`, define a render mode at the top of the `Home.razor` page. + +N> If an Interactivity Location is set to `Global` and the **Render Mode** is set to `Server`, the render mode is configured in the `App.razor` file by default. ``` @* desired render mode define here *@ @@ -211,41 +217,62 @@ N> If the interactivity location is set to `Global` and the **Render Mode** is ` {% endhighlight %} {% endtabs %} -* Press Ctrl+F5 (Windows) or +F5 (macOS) to launch the application. This renders the Syncfusion® Blazor File Upload component in the default web browser. +#### Preview + +* Press Ctrl+F5 (Windows) or +F5 (macOS) to launch the application. This will render the Syncfusion® Blazor File Upload component in your default web browser. + +{% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -{% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" backgroundimage "[Blazor FileUpload Component](./images/blazor-fileupload-component.png)" %} +![Blazor File Upload Basic Component](images/blazor-fileupload-basic.gif) -## Without server-side API endpoint +## Use ValueChange Event -Upload the files and files of folders in the Blazor application without specifying the server-side API end point using [AsyncSettings](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderAsyncSettings.html). +The [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event fires when files are selected or removed from the uploader. This event is crucial for client-side processing of selected files, allowing you to access file details and their content, which is useful for previewing files or handling uploads without a server-side endpoint. -### Save and Remove actions +### Code Example -Access the uploaded files as streams in the [ValueChange](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event argument. Implement save logic in the ValueChange event to write files to the desired location. The following sample shows the save action pattern. +This example demonstrates how to use the [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event to save uploaded files directly to a local directory when the [`AutoUpload`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.SfUploader.html#Syncfusion_Blazor_Inputs_SfUploader_AutoUpload) property is set to `true`. This is useful for scenarios where you want to process files immediately after selection without an explicit upload button. {% tabs %} {% highlight razor %} @using Syncfusion.Blazor.Inputs +@using System.IO + + @code { private async Task OnChange(UploadChangeEventArgs args) { try { - foreach (var file in args.Files) + foreach (var fileEntry in args.Files) { - var path = @"" + file.FileInfo.Name; - FileStream filestream = new FileStream(path, FileMode.Create, FileAccess.Write); - await file.File.OpenReadStream(long.MaxValue).CopyToAsync(filestream); - filestream.Close(); + // Define a path where you want to save the file. + // For a Blazor Server app, this path will be on the server. + var uploadsFolder = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads"); + if (!Directory.Exists(uploadsFolder)) + { + Directory.CreateDirectory(uploadsFolder); + } + + // Construct the full file path. + var filePath = Path.Combine(uploadsFolder, fileEntry.FileInfo.Name); + + // Use a FileStream to write the uploaded file's content to the server. + await using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) + { + // OpenReadStream with long.MaxValue allows reading the entire stream. + await fileEntry.File.OpenReadStream(long.MaxValue).CopyToAsync(fileStream); + } + Console.WriteLine($"File '{fileEntry.FileInfo.Name}' saved successfully to '{filePath}'"); } } catch (Exception ex) { - Console.WriteLine(ex.Message); + Console.WriteLine($"Error saving file: {ex.Message}"); } } } @@ -253,20 +280,73 @@ Access the uploaded files as streams in the [ValueChange](https://help.syncfusio {% endhighlight %} {% endtabs %} -![Blazor FileUpload displays Updated Files](./images/blazor-fileupload-with-updated-files.png) +N> When saving files directly in a Blazor Server application using [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) and [`AutoUpload`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.SfUploader.html#Syncfusion_Blazor_Inputs_SfUploader_AutoUpload), the files are saved on the server where the Blazor Server app is running, not on the client's machine. You need appropriate file system permissions for the server process to write to the specified directory. Also, ensure the target directory (`wwwroot/uploads` in this example) exists or is created programmatically. In a production environment, consider secure storage solutions for uploaded files. + +### Preview + +{% previewsample "https://blazorplayground.syncfusion.com/embed/hDVyZkrqBvaSlvht?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} + +![Blazor File Upload ValueChange Event](images/blazor-fileupload-valuechange.gif) + -When the remove icon is selected in the file list, the [OnRemove](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnRemove) event provides the file name as an argument. Implement the remove handler in the OnRemove event to delete the file from the chosen location. The following sample illustrates the remove action pattern. +## Memory stream + +When you need to process uploaded files in memory—perhaps for resizing images, reading content, or sending them to another service without saving them to disk first—using a `MemoryStream` is an efficient approach. This is particularly useful for temporary processing or when dealing with sensitive data that shouldn't persist on the file system. + +### Code Example + +This example demonstrates how to read the content of an uploaded file into a [MemoryStream Class](https://learn.microsoft.com/en-us/dotnet/api/system.io.memorystream)`. This allows you to perform in-memory operations on the file, such as converting an image to a Base64 string, without requiring disk I/O. {% tabs %} -{% highlight cshtml %} +{% highlight razor %} + +@using Syncfusion.Blazor.Inputs +@using System.IO -Private void onRemove(RemovingEventArgs args) + + + + +@if (!string.IsNullOrEmpty(base64Image)) { - foreach(var removeFile in args.FilesData) +

Uploaded Image Preview (Base64)

+ +} + +@code { + private string base64Image; + + private async Task OnValueChangeMemoryStream(UploadChangeEventArgs args) { - if (File.Exists(Path.Combine(@"rootPath", removeFile.Name))) + base64Image = string.Empty; // Clear previous image + + foreach (var fileEntry in args.Files) { - File.Delete(Path.Combine(@"rootPath", removeFile.Name)) + if (fileEntry.FileInfo.Type.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) + { + // Create a MemoryStream to hold the file content. + using var memoryStream = new MemoryStream(); + + // Copy the file's content from the upload stream to the MemoryStream. + await fileEntry.File.OpenReadStream(long.MaxValue).CopyToAsync(memoryStream); + + // Convert the MemoryStream content to a byte array. + byte[] imageBytes = memoryStream.ToArray(); + + // Convert byte array to Base64 string for display or further processing. + base64Image = Convert.ToBase64String(imageBytes); + Console.WriteLine($"Image '{fileEntry.FileInfo.Name}' loaded into MemoryStream and converted to Base64."); + } + else + { + Console.WriteLine($"File '{fileEntry.FileInfo.Name}' is not an image and won't be processed for Base64 preview."); + // For non-image files, you could read their content as text or handle differently. + // Example for text file: + // memoryStream.Position = 0; // Reset stream position + // using var reader = new StreamReader(memoryStream); + // var content = await reader.ReadToEndAsync(); + // Console.WriteLine($"Content of {fileEntry.FileInfo.Name}: {content}"); + } } } } @@ -274,114 +354,153 @@ Private void onRemove(RemovingEventArgs args) {% endhighlight %} {% endtabs %} -## With server-side API endpoint +N> When using `MemoryStream` for large files, be mindful of server memory consumption. If you expect very large files, consider processing them in chunks or saving them to temporary storage before processing to avoid out-of-memory exceptions. The `long.MaxValue` in `OpenReadStream` indicates the maximum buffer size. In a Blazor Server app, `Stream` operations occur on the server. + +#### Preview + +{% previewsample "https://blazorplayground.syncfusion.com/embed/BjreNaLUhdwxzvHS?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} + +#### Gif + +![Blazor File Upload Memory Stream Example](images/blazor-fileupload-memorystream.gif) -The upload process requires save and remove action URL to manage the upload process in the server. -N> * The save action is necessary to handle the upload operation. -
* The remove action is optional, one can handle the removed files from server. +## Created Event -The save action handler upload the files that needs to be specified in the [SaveUrl](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderAsyncSettings.html#Syncfusion_Blazor_Inputs_UploaderAsyncSettings_SaveUrl) property. +The [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event fires after the File Upload component has been rendered and initialized. This event is a good place to perform any initial setup, attach custom event listeners to the component's DOM elements, or apply custom styling that requires the component to be fully rendered. -The save handler receives the submitted files and manages the save process in server. After uploading the files to server location, the color of the selected file name changes to green and the remove icon is changed as bin icon. +### Code Example -Specify the remove endpoint in the [RemoveUrl](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderAsyncSettings.html#Syncfusion_Blazor_Inputs_UploaderAsyncSettings_RemoveUrl) property. The [OnActionComplete](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnActionComplete) event is triggered after all selected files have been processed (uploaded successfully or failed). +This example shows how to use the [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event to add a custom message or dynamically change some aspect of the uploader's appearance right after it's created. {% tabs %} -{% highlight cshtml %} +{% highlight razor %} -[Route("api/[controller]")] -public class SampleDataController : Controller -{ - public string uploads = ".\\Uploaded Files"; // replace with your directory path +@using Syncfusion.Blazor.Inputs - [HttpPost("[action]")] - public async Task Save(IFormFile UploadFiles) // Save the uploaded file here - { - if (UploadFiles.Length > 0) - { - //Create directory if not exists - if (!Directory.Exists(uploads)) - { - Directory.CreateDirectory(uploads); - } + + + - var filePath = Path.Combine(uploads, UploadFiles.FileName); - if (System.IO.File.Exists(filePath)) - { - //Return conflict status code - return new StatusCodeResult(StatusCodes.Status409Conflict); - } - using (var fileStream = new FileStream(filePath, FileMode.Create)) - { - //Save the uploaded file to server - await UploadFiles.CopyToAsync(fileStream); - } - } - return Ok(); - } +

@statusMessage

+@code { + private string statusMessage = "Uploader not yet created."; - [HttpPost("[action]")] - public void Remove(string UploadFiles) // Delete the uploaded file here + private void OnUploaderCreated() { - if(UploadFiles != null) - { - var filePath = Path.Combine(uploads, UploadFiles); - if (System.IO.File.Exists(filePath)) - { - //Delete the file from server - System.IO.File.Delete(filePath); - } - } + statusMessage = "Syncfusion File Uploader has been successfully created and initialized!"; + Console.WriteLine(statusMessage); + // You could also interact with JavaScript to modify DOM here if needed. + // For example: JSRuntime.InvokeVoidAsync("someJsFunction"); } } {% endhighlight %} {% endtabs %} -The [OnFailure](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnFailure) event is triggered when there is a failure in the AJAX request during uploading or removing files. Use it to handle and respond to errors that occur during the upload or removal process. +N> The [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event is useful for client-side JavaScript interop if you need to manipulate the DOM elements of the uploader component immediately after it's ready. However, for most Blazor-specific customizations (like custom templates), you should use the built-in Blazor features. + +### Preview + +{% previewsample "https://blazorplayground.syncfusion.com/embed/VtLyNuVUBGtPZrdo?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} + +![Blazor File Upload Created Example](images/blazor-fileupload-created.gif) + + +## File Selected Event + +The [`FileSelected Event`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_FileSelected)event is triggered when files are chosen from the file explorer dialog, but **before** the [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event. This event provides an opportunity to perform validations on the selected files (e.g., file size, type, count) and decide whether to proceed with the upload/value change or cancel the selection. It's ideal for immediate client-side feedback or preventative actions. + +### Code Example + +This example demonstrates how to use the [FileSelected Event](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_FileSelected) event to prevent files larger than a certain size. {% tabs %} {% highlight razor %} - - - +@using Syncfusion.Blazor.Inputs +@using System.Linq + + + +

@validationMessage

+ @code { - private void OnFailureHandler(FailureEventArgs args) - { - // Here, you can customize your code. - } - private void OnActionCompleteHandler(ActionCompleteEventArgs args) + private string validationMessage = ""; + private readonly long MaxFileSize = 1024 * 1024; // 1 MB + + private void OnFileSelected(SelectedEventArgs args) { - // Here, you can customize your code. + validationMessage = ""; + foreach (var file in args.FilesData) + { + if (file.Size > MaxFileSize) + { + validationMessage += $"Error: File '{file.Name}' exceeds {MaxFileSize / (1024 * 1024)} MB limit. "; + args.Cancel = true; // Prevents this file from being added + } + } + if (!string.IsNullOrEmpty(validationMessage)) + { + Console.WriteLine(validationMessage); + } + else + { + Console.WriteLine("Files selected successfully and passed initial validation."); + } } } {% endhighlight %} {% endtabs %} -## Configure allowed file types +N> Setting `args.Cancel = true` in the `FileSelected` event will prevent the file (or files if `args.Files` contains multiple) from being added to the uploader's internal file list. This is a client-side validation and should be complemented with server-side validation for robust security and data integrity. + +### Preview + +{% previewsample "https://blazorplayground.syncfusion.com/embed/BDLIZuBUVwEJoJpz?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} + +![Blazor File Upload File Selected Example](images/blazor-fileupload-fileselected.gif) + -Allow only specific files to be uploaded using the [AllowedExtensions](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderModel.html#Syncfusion_Blazor_Inputs_UploaderModel_AllowedExtensions) property. Provide extensions as a comma-separated list. The Uploader filters selected or dropped files to match the specified types and then processes the upload operation. Client-side validation occurs when a value is specified for the underlying input element’s accept attribute. +## OnFileListRender + +The [`OnFileListRender`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnFileListRender) event allows you to customize individual file list items before they are rendered in the uploader's UI. This is highly useful for scenarios where you need to display additional information alongside each file, such as a custom preview, metadata, or actions. + +### Code Example + +This example demonstrates how to use [`OnFileListRender`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnFileListRender) {% tabs %} {% highlight razor %} - +@using Syncfusion.Blazor.Inputs + + + + + + +@code { + SfUploader fileobj; + private void OnFileListRenderHandler(FileListRenderingEventArgs args) + { + + } +} {% endhighlight %} {% endtabs %} -{% previewsample "https://blazorplayground.syncfusion.com/embed/rXhzDsrOqbKVNviI?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" backgroundimage "[Allowing Specific Files in Blazor FileUpload](./images/blazor-fileupload-allow-specific-file.png)" %} N> [View Sample in GitHub](https://github.com/SyncfusionExamples/Blazor-Getting-Started-Examples/tree/main/FileUpload). ## See also -1. [Getting Started with Syncfusion® Blazor for client-side in .NET Core CLI](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-dotnet-cli) -2. [Getting Started with Syncfusion® Blazor for client-side in Visual Studio](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-visual-studio) -3. [Getting Started with Syncfusion® Blazor for server-side in .NET Core CLI](https://blazor.syncfusion.com/documentation/getting-started/blazor-server-side-dotnet-cli) -4. [Getting Started with Syncfusion® File Upload in Blazor WebAssembly using Visual Studio](https://blazor.syncfusion.com/documentation/file-upload/how-to/getting-started-with-blazor-webassembly) \ No newline at end of file +1. [Getting Started with Syncfusion® Blazor for client-side in .NET Core CLI](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-dotnet-cli) +2. [Getting Started with Syncfusion® Blazor for client-side in Visual Studio](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-visual-studio) +3. [Getting Started with Syncfusion® Blazor for server-side in .NET Core CLI](https://blazor.syncfusion.com/documentation/getting-started/blazor-server-side-dotnet-cli) +4. [Getting Started with Syncfusion® File Upload in Blazor WebAssembly using Visual Studio](https://blazor.syncfusion.com/documentation/file-upload/how-to/getting-started-with-blazor-webassembly) +5. [How to convert images to Base64 string with Blazor File Upload](https://support.syncfusion.com/kb/article/21178/how-to-convert-images-to-base64-string-with-blazor-file-upload) diff --git a/blazor/file-upload/getting-started-with-web-app.md b/blazor/file-upload/getting-started-with-web-app.md index c3c31f179f..d2338a7ffa 100644 --- a/blazor/file-upload/getting-started-with-web-app.md +++ b/blazor/file-upload/getting-started-with-web-app.md @@ -9,7 +9,7 @@ documentation: ug # Getting Started with Blazor File Upload Component in Web App -This section explains how to include the [Blazor File Upload](https://www.syncfusion.com/blazor-components/blazor-file-upload) component in a Blazor Web App using Visual Studio, Visual Studio Code, and the .NET CLI. +This section briefly explains how to include the [Blazor File Upload](https://www.syncfusion.com/blazor-components/blazor-file-upload) component in your Blazor Web App using [Visual Studio](https://visualstudio.microsoft.com/vs/), Visual Studio Code, and .NET CLI. {% tabcontents %} @@ -17,23 +17,21 @@ This section explains how to include the [Blazor File Upload](https://www.syncfu ## Prerequisites -* [System requirements for Blazor components](https://blazor.syncfusion.com/documentation/system-requirements) +* [System requirements for Blazor components](https://blazor.syncfusion.com/documentation/system-requirements) ## Create a new Blazor Web App in Visual Studio -Create a **Blazor Web App** using Visual Studio 2022 via [Microsoft templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-8.0&pivots=vs) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-integration/template-studio). For detailed instructions, refer to [this Blazor Web App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/blazor-web-app) documentation. +You can create a **Blazor Web App** using Visual Studio 2022 via [Microsoft Templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-8.0&pivots=vs) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-integration/template-studio). For detailed instructions, refer to [this Blazor Web App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/blazor-web-app) documentation. -Configure the appropriate [interactive render mode](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?view=aspnetcore-8.0#render-modes) and [interactivity location](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-8.0&pivots=vs) when creating the project. - -![Create Blazor Web App](images/blazor-create-web-app.png) +You need to configure the corresponding [Interactive render mode](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?view=aspnetcore-8.0#render-modes) and [Interactivity location](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-8.0&pivots=vs) while creating a Blazor Web Application. ## Install Syncfusion® Blazor Inputs and Themes NuGet in the Blazor Web App -To add the **Blazor File Upload** component, open NuGet Package Manager in Visual Studio (Tools → NuGet Package Manager → Manage NuGet Packages for Solution), then install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs/) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/). +To add the **Blazor File Upload** component in the app, open the NuGet package manager in Visual Studio (*Tools → NuGet Package Manager → Manage NuGet Packages for Solution*), search and install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs/) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/). -If using the `WebAssembly` or `Auto` render modes, install the Syncfusion® Blazor NuGet packages in the client project. +If you utilize `WebAssembly` or `Auto` render modes in the Blazor Web App, you need to install Syncfusion® Blazor components NuGet packages within the client project. -Alternatively, use the following Package Manager commands. +Alternatively, you can utilize the following package manager command to achieve the same. {% tabs %} {% highlight C# tabtitle="Package Manager" %} @@ -44,7 +42,7 @@ Install-Package Syncfusion.Blazor.Themes -Version {{ site.releaseversion }} {% endhighlight %} {% endtabs %} -N> Syncfusion® Blazor components are available on [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). For a full list of packages and component details, see the [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic. +N> Syncfusion® Blazor components are available in [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). Refer to [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for available NuGet packages list with component details. {% endtabcontent %} @@ -52,18 +50,18 @@ N> Syncfusion® Blazor components are availa ## Prerequisites -* [System requirements for Blazor components](https://blazor.syncfusion.com/documentation/system-requirements) +* [System requirements for Blazor components](https://blazor.syncfusion.com/documentation/system-requirements) ## Create a new Blazor Web App in Visual Studio Code -Create a **Blazor Web App** using Visual Studio Code via [Microsoft templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-8.0&pivots=vsc) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-code-integration/create-project). For detailed instructions, refer to [this Blazor Web App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/blazor-web-app?tabcontent=visual-studio-code) documentation. +You can create a **Blazor Web App** using Visual Studio Code via [Microsoft Templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-8.0&pivots=vsc) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-code-integration/create-project). For detailed instructions, refer to [this Blazor Web App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/blazor-web-app?tabcontent=visual-studio-code) documentation. -Configure the appropriate interactive render mode and interactivity location during setup. For details, see the [interactive render mode documentation](https://blazor.syncfusion.com/documentation/common/interactive-render-mode). +Configure the appropriate interactive render mode and interactivity location when setting up a Blazor Web Application. For detailed information, refer to the [interactive render mode documentation](https://blazor.syncfusion.com/documentation/common/interactive-render-mode). -For example, to create a Blazor Web App with the `Auto` interactive render mode: +For example, in a Blazor Web App with the `Auto` interactive render mode, use the following commands. {% tabs %} -{% highlight c# tabtitle="Blazor Web App" %} +{% highlight C# tabtitle="Blazor Web App" %} dotnet new blazor -o BlazorWebApp -int Auto cd BlazorWebApp @@ -72,17 +70,19 @@ cd BlazorWebApp.Client {% endhighlight %} {% endtabs %} +N> For more information on creating a **Blazor Web App** with various interactive modes and locations, refer to this [link](https://blazor.syncfusion.com/documentation/getting-started/blazor-web-app?tabcontent=visual-studio-code#render-interactive-modes). + ## Install Syncfusion® Blazor Inputs and Themes NuGet in the App -If using the `WebAssembly` or `Auto` render modes, install Syncfusion® Blazor NuGet packages in the client project. +If you utilize `WebAssembly` or `Auto` render modes in the Blazor Web App, you need to install Syncfusion® Blazor components NuGet packages within the client project. -* Press Ctrl+` to open the integrated terminal in Visual Studio Code. -* Ensure you are in the project directory that contains the target `.csproj` file. -* Run the following commands to install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/), then restore packages. +* Press Ctrl+` to open the integrated terminal in Visual Studio Code. +* Ensure you’re in the project root directory where your `.csproj` file is located. +* Run the following command to install a [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/) NuGet package and ensure all dependencies are installed. {% tabs %} -{% highlight c# tabtitle="Package Manager" %} +{% highlight C# tabtitle="Package Manager" %} dotnet add package Syncfusion.Blazor.Inputs -v {{ site.releaseversion }} dotnet add package Syncfusion.Blazor.Themes -v {{ site.releaseversion }} @@ -92,7 +92,7 @@ dotnet restore {% endtabs %} -N> Syncfusion® Blazor components are available on [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). For the complete package list with component details, see the [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic. +N> Syncfusion® Blazor components are available in [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). Refer to [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for available NuGet packages list with component details. {% endtabcontent %} @@ -100,26 +100,26 @@ N> Syncfusion® Blazor components are availa ## Prerequisites -Install the latest [.NET SDK](https://dotnet.microsoft.com/en-us/download). To check the installed version, run the following command in a terminal or command prompt: +Latest version of the [.NET Core SDK](https://dotnet.microsoft.com/en-us/download). If you previously installed the SDK, you can determine the installed version by executing the following command in a command prompt (Windows) or terminal (macOS) or command shell (Linux). {% tabs %} -{% highlight c# tabtitle=".NET CLI" %} +{% highlight C# tabtitle=".NET CLI" %} dotnet --version {% endhighlight %} {% endtabs %} -## Create a Blazor Web App using .NET CLI +## Create a Blazor Web project using .NET CLI -Run the following command to create a new Blazor Web App. For step-by-step guidance, see the [Blazor Web App getting started](https://blazor.syncfusion.com/documentation/getting-started/blazor-web-app?tabcontent=.net-cli) documentation. +Run the following command to create a new Blazor Web App in a command prompt (Windows) or terminal (macOS) or command shell (Linux). For detailed instructions, refer to [this Blazor Web App Getting Started](https://blazor.syncfusion.com/documentation/getting-started/blazor-web-app?tabcontent=.net-cli) documentation. -Configure the appropriate interactive render mode and interactivity location during setup. For details, see the [interactive render mode documentation](https://blazor.syncfusion.com/documentation/common/interactive-render-mode). +Configure the appropriate interactive render mode and interactivity location when setting up a Blazor Web Application. For detailed information, refer to the [interactive render mode documentation](https://blazor.syncfusion.com/documentation/common/interactive-render-mode). -For example, to create a Blazor Web App with the `Auto` interactive render mode: +For example, in a Blazor Web App with `Auto` interactive render mode, use the following commands: {% tabs %} -{% highlight c# tabtitle=".NET CLI" %} +{% highlight C# tabtitle=".NET CLI" %} dotnet new blazor -o BlazorApp -int Auto cd BlazorApp @@ -128,16 +128,16 @@ cd BlazorApp.Client {% endhighlight %} {% endtabs %} -This command creates a new Blazor Web App in a directory named `BlazorApp` within the current location. See the [Create Blazor app](https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/create) and [dotnet new](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?pivots=linux-macos&view=aspnetcore-8.0) topics for more details. +This command creates a new Blazor Web app project and places it in a new directory called `BlazorApp` inside your current location. See [Create Blazor app topic](https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/create) and [dotnet new CLI command](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?pivots=linux-macos&view=aspnetcore-8.0) topics for more details. ## Install Syncfusion® Blazor Inputs and Themes NuGet in the App -Here's an example of how to add **Blazor File Upload** component in the application using the following command in the command prompt (Windows) or terminal (Linux and macOS) to install a [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs/) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/) NuGet package. See [Install and manage packages using the dotnet CLI](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-dotnet-cli) topics for more details. +Here's an example of how to add the **Blazor File Upload** component in the application using the following command in the command prompt (Windows) or terminal (Linux and macOS) to install a [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs/) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/) NuGet package. See [Install and manage packages using the dotnet CLI](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-dotnet-cli) topics for more details. -If using the `WebAssembly` or `Auto` render modes, install packages in the client project. +If you utilize `WebAssembly` or `Auto` render modes in the Blazor Web App, you need to install Syncfusion® Blazor components NuGet packages within the client project. {% tabs %} -{% highlight c# tabtitle=".NET CLI" %} +{% highlight C# tabtitle=".NET CLI" %} dotnet add package Syncfusion.Blazor.Inputs --version {{ site.releaseversion }} dotnet add package Syncfusion.Blazor.Themes --version {{ site.releaseversion }} @@ -146,7 +146,7 @@ dotnet restore {% endhighlight %} {% endtabs %} -N> Syncfusion® Blazor components are available on [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). For a complete package list, see the [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic. +N> Syncfusion® Blazor components are available in [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). Refer to [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for available NuGet packages list with component details. {% endtabcontent %} @@ -154,7 +154,7 @@ N> Syncfusion® Blazor components are availa ## Add Import Namespaces -Open the **~/_Imports.razor** file in the client project and import the `Syncfusion.Blazor` and `Syncfusion.Blazor.Inputs` namespaces. +Open the **~/_Imports.razor** file from the client project and import the `Syncfusion.Blazor` and `Syncfusion.Blazor.Inputs` namespaces. {% tabs %} {% highlight C# tabtitle="~/_Imports.razor" %} @@ -165,14 +165,14 @@ Open the **~/_Imports.razor** file in the client project and import the `Syncfus {% endhighlight %} {% endtabs %} -## Register Syncfusion® Blazor Service +

Register Syncfusion® Blazor Service

-Register the Syncfusion® Blazor service in the **~/Program.cs** file of the Blazor Web App. +Register the Syncfusion® Blazor Service in the **~/Program.cs** file of your Blazor Web App. -If the app uses `WebAssembly` or `Auto` interactive render modes, register the Syncfusion® Blazor service in both the main Server project and the associated `.Client` project. +If your Blazor Web App uses `WebAssembly` or `Auto` interactive render modes, you must register the Syncfusion® Blazor service in the **~/Program.cs** files of the main `server` project and associated `.Client` project. {% tabs %} -{% highlight c# tabtitle="Server(~/_Program.cs)" hl_lines="3 11" %} +{% highlight C# tabtitle="Server (~/Program.cs)" hl_lines="3 11" %} ... ... @@ -190,7 +190,7 @@ var app = builder.Build(); .... {% endhighlight %} -{% highlight c# tabtitle="Client(~/_Program.cs)" hl_lines="2 5" %} +{% highlight C# tabtitle="Client (~/Program.cs)" hl_lines="2 5" %} ... using Syncfusion.Blazor; @@ -203,9 +203,9 @@ await builder.Build().RunAsync(); {% endhighlight %} {% endtabs %} -## Add stylesheet and script resources +

Add stylesheet and script resources

-The theme stylesheet and script are available via [Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets). Add the stylesheet in the `` and the script at the end of the `` in **~/Components/App.razor** as shown below. When using the `WebAssembly` or `Auto` render modes, ensure the client project also references the required resources. +The theme stylesheet and script can be accessed from NuGet through [Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets). Include the stylesheet reference in the `` section and the script reference at the end of the `` in the **~/Components/App.razor** file as shown below: ```html @@ -217,16 +217,20 @@ The theme stylesheet and script are available via [Static Web Assets](https://bl .... - //Blazor File Upload Component script reference. - + + ``` -N> See the [Blazor Themes](https://blazor.syncfusion.com/documentation/appearance/themes) topic for ways to reference themes ([Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets), [CDN](https://blazor.syncfusion.com/documentation/appearance/themes#cdn-reference), and [CRG](https://blazor.syncfusion.com/documentation/common/custom-resource-generator)). For adding scripts, see [Adding Script Reference](https://blazor.syncfusion.com/documentation/common/adding-script-references). If using the consolidated Syncfusion script, separate per-component scripts are typically not required. +N> Check out the [Blazor Themes](https://blazor.syncfusion.com/documentation/appearance/themes) topic to discover various methods ([Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets), [CDN](https://blazor.syncfusion.com/documentation/appearance/themes#cdn-reference), and [CRG](https://blazor.syncfusion.com/documentation/common/custom-resource-generator)) for referencing themes in your Blazor application. Also, check out the [Adding Script Reference](https://blazor.syncfusion.com/documentation/common/adding-script-references) topic to learn different approaches for adding script references in your Blazor application. -## Add Syncfusion® Blazor File Upload component +## Add Syncfusion® Blazor File Upload component -Add the Syncfusion® Blazor File Upload component to a Razor page under the Pages folder (for example, Pages/Home.razor) in the **Server** or **Client** project. If the interactivity location is set to `Per page/component`, define a render mode at the top of the component as shown: +The Syncfusion Blazor File Upload component allows you to seamlessly integrate file upload functionalities into your Blazor applications. It supports various features like asynchronous and synchronous uploads, file type validation, progress tracking, and custom templates. A common use case is enabling users to upload documents, images, or other files to a server, or process them directly within the client-side application. + +### Simple Code to render a Usable File Upload Component + +The most basic way to render the File Upload component is by adding the `` tag to your `.razor` page. By default, this component provides a clean interface for users to select files locally. | Interactivity location | RenderMode | Code | | --- | --- | --- | @@ -234,7 +238,7 @@ Add the Syncfusion® Blazor File Upload comp | | WebAssembly | @rendermode InteractiveWebAssembly | | | None | --- | -N> If the **Interactivity Location** is set to `Global` and the **Render Mode** is `Auto` or `WebAssembly`, the render mode is configured in `App.razor` by default. +N> If an **Interactivity Location** is set to `Global` and the **Render Mode** is set to `Auto` or `WebAssembly`, the render mode is configured in the `App.razor` file by default. {% tabs %} {% highlight razor %} @@ -253,41 +257,66 @@ N> If the **Interactivity Location** is set to `Global` and the **Render Mode** {% endhighlight %} {% endtabs %} -* Press Ctrl+F5 (Windows) or +F5 (macOS) to run the application. The Syncfusion® Blazor File Upload component should render in the browser. +* Press Ctrl+F5 (Windows) or +F5 (macOS) to launch the application. This will render the Syncfusion® Blazor File Upload component in your default web browser. + +{% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -{% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" backgroundimage "[Blazor FileUpload Component](./images/blazor-fileupload-component.png)" %} +#### Preview -## Without server-side API endpoint +* Press Ctrl+F5 (Windows) or +F5 (macOS) to launch the application. This will render the Syncfusion® Blazor File Upload component in your default web browser. -Upload files (and folder contents) without specifying a server-side API endpoint by using [AsyncSettings](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderAsyncSettings.html) and handling file streams in component events. This approach is suitable for local/demo scenarios. In production, validate file types and sizes, use unique file names, and store files in a secure, dedicated directory. +{% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -### Save and Remove actions +![Blazor File Upload Basic Component](images/blazor-fileupload-basic.gif) -Access uploaded files as streams in the [ValueChange](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event and write them to the desired location. The following sample demonstrates the save action. +## Use ValueChange Event + +The [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event fires when files are selected or removed from the uploader. This event is crucial for client-side processing of selected files, allowing you to access file details and their content, which is useful for previewing files or handling uploads without a server-side endpoint. + +### Code Example + +This example demonstrates how to use the [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event to save uploaded files directly to a local directory when the [`AutoUpload`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.SfUploader.html#Syncfusion_Blazor_Inputs_SfUploader_AutoUpload) property is set to `true`. This is useful for scenarios where you want to process files immediately after selection without an explicit upload button. {% tabs %} {% highlight razor %} @using Syncfusion.Blazor.Inputs +@using System.IO + + @code { private async Task OnChange(UploadChangeEventArgs args) { try { - foreach (var file in args.Files) + foreach (var fileEntry in args.Files) { - var path = @"" + file.FileInfo.Name; - FileStream filestream = new FileStream(path, FileMode.Create, FileAccess.Write); - await file.File.OpenReadStream(long.MaxValue).CopyToAsync(filestream); - filestream.Close(); + // Define a path where you want to save the file. + // For a Blazor Server app, this path will be on the server. + var uploadsFolder = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads"); + if (!Directory.Exists(uploadsFolder)) + { + Directory.CreateDirectory(uploadsFolder); + } + + // Construct the full file path. + var filePath = Path.Combine(uploadsFolder, fileEntry.FileInfo.Name); + + // Use a FileStream to write the uploaded file's content to the server. + await using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) + { + // OpenReadStream with long.MaxValue allows reading the entire stream. + await fileEntry.File.OpenReadStream(long.MaxValue).CopyToAsync(fileStream); + } + Console.WriteLine($"File '{fileEntry.FileInfo.Name}' saved successfully to '{filePath}'"); } } catch (Exception ex) { - Console.WriteLine(ex.Message); + Console.WriteLine($"Error saving file: {ex.Message}"); } } } @@ -295,20 +324,73 @@ Access uploaded files as streams in the [ValueChange](https://help.syncfusion.co {% endhighlight %} {% endtabs %} -![Blazor FileUpload displays Updated Files](./images/blazor-fileupload-with-updated-files.png) +N> When saving files directly in a Blazor Server application using [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) and [`AutoUpload`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.SfUploader.html#Syncfusion_Blazor_Inputs_SfUploader_AutoUpload), the files are saved on the server where the Blazor Server app is running, not on the client's machine. You need appropriate file system permissions for the server process to write to the specified directory. Also, ensure the target directory (`wwwroot/uploads` in this example) exists or is created programmatically. In a production environment, consider secure storage solutions for uploaded files. + +### Preview -When selecting the remove icon in the file list, the [OnRemove](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnRemove) event provides the file name to remove. Implement logic in the OnRemove event to delete the corresponding file from the chosen storage location. The following sample illustrates the remove action pattern. +{% previewsample "https://blazorplayground.syncfusion.com/embed/hDVyZkrqBvaSlvht?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} + +![Blazor File Upload ValueChange Event](images/blazor-fileupload-valuechange.gif) + + +## Memory stream + +When you need to process uploaded files in memory—perhaps for resizing images, reading content, or sending them to another service without saving them to disk first—using a `MemoryStream` is an efficient approach. This is particularly useful for temporary processing or when dealing with sensitive data that shouldn't persist on the file system. + +### Code Example + +This example demonstrates how to read the content of an uploaded file into a [MemoryStream Class](https://learn.microsoft.com/en-us/dotnet/api/system.io.memorystream)`. This allows you to perform in-memory operations on the file, such as converting an image to a Base64 string, without requiring disk I/O. {% tabs %} -{% highlight cshtml %} +{% highlight razor %} + +@using Syncfusion.Blazor.Inputs +@using System.IO + + + + -Private void onRemove(RemovingEventArgs args) +@if (!string.IsNullOrEmpty(base64Image)) { - foreach(var removeFile in args.FilesData) +

Uploaded Image Preview (Base64)

+ +} + +@code { + private string base64Image; + + private async Task OnValueChangeMemoryStream(UploadChangeEventArgs args) { - if (File.Exists(Path.Combine(@"rootPath", removeFile.Name))) + base64Image = string.Empty; // Clear previous image + + foreach (var fileEntry in args.Files) { - File.Delete(Path.Combine(@"rootPath", removeFile.Name)) + if (fileEntry.FileInfo.Type.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) + { + // Create a MemoryStream to hold the file content. + using var memoryStream = new MemoryStream(); + + // Copy the file's content from the upload stream to the MemoryStream. + await fileEntry.File.OpenReadStream(long.MaxValue).CopyToAsync(memoryStream); + + // Convert the MemoryStream content to a byte array. + byte[] imageBytes = memoryStream.ToArray(); + + // Convert byte array to Base64 string for display or further processing. + base64Image = Convert.ToBase64String(imageBytes); + Console.WriteLine($"Image '{fileEntry.FileInfo.Name}' loaded into MemoryStream and converted to Base64."); + } + else + { + Console.WriteLine($"File '{fileEntry.FileInfo.Name}' is not an image and won't be processed for Base64 preview."); + // For non-image files, you could read their content as text or handle differently. + // Example for text file: + // memoryStream.Position = 0; // Reset stream position + // using var reader = new StreamReader(memoryStream); + // var content = await reader.ReadToEndAsync(); + // Console.WriteLine($"Content of {fileEntry.FileInfo.Name}: {content}"); + } } } } @@ -316,109 +398,146 @@ Private void onRemove(RemovingEventArgs args) {% endhighlight %} {% endtabs %} -## With server-side API endpoint +N> When using `MemoryStream` for large files, be mindful of server memory consumption. If you expect very large files, consider processing them in chunks or saving them to temporary storage before processing to avoid out-of-memory exceptions. The `long.MaxValue` in `OpenReadStream` indicates the maximum buffer size. In a Blazor Server app, `Stream` operations occur on the server. + +#### Preview + +{% previewsample "https://blazorplayground.syncfusion.com/embed/BjreNaLUhdwxzvHS?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} + +#### Gif -The upload process requires save and remove action URL to manage the upload process in the server. +![Blazor File Upload Memory Stream Example](images/blazor-fileupload-memorystream.gif) -N> * The save action is necessary to handle the upload operation. -
* The remove action is optional, one can handle the removed files from server. -N> * The save action is required to process uploaded files on the server. -
* The remove action is optional and is used to delete files from the server. +## Created Event -The save handler receives the submitted files and manages the save process in server. After uploading the files to server location, the color of the selected file name changes to green and the remove icon is changed as bin icon. +The [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event fires after the File Upload component has been rendered and initialized. This event is a good place to perform any initial setup, attach custom event listeners to the component's DOM elements, or apply custom styling that requires the component to be fully rendered. -Specify the remove endpoint in the [RemoveUrl](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderAsyncSettings.html#Syncfusion_Blazor_Inputs_UploaderAsyncSettings_RemoveUrl) property. The [OnActionComplete](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnActionComplete) event triggers after all selected files are processed (success or failure). +### Code Example + +This example shows how to use the [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event to add a custom message or dynamically change some aspect of the uploader's appearance right after it's created. {% tabs %} -{% highlight cshtml %} +{% highlight razor %} -[Route("api/[controller]")] -public class SampleDataController : Controller -{ - public string uploads = ".\\Uploaded Files"; // replace with your directory path +@using Syncfusion.Blazor.Inputs - [HttpPost("[action]")] - public async Task Save(IFormFile UploadFiles) // Save the uploaded file here - { - if (UploadFiles.Length > 0) - { - //Create directory if not exists - if (!Directory.Exists(uploads)) - { - Directory.CreateDirectory(uploads); - } + + + - var filePath = Path.Combine(uploads, UploadFiles.FileName); - if (System.IO.File.Exists(filePath)) - { - //Return conflict status code - return new StatusCodeResult(StatusCodes.Status409Conflict); - } - using (var fileStream = new FileStream(filePath, FileMode.Create)) - { - //Save the uploaded file to server - await UploadFiles.CopyToAsync(fileStream); - } - } - return Ok(); - } +

@statusMessage

+@code { + private string statusMessage = "Uploader not yet created."; - [HttpPost("[action]")] - public void Remove(string UploadFiles) // Delete the uploaded file here + private void OnUploaderCreated() { - if(UploadFiles != null) - { - var filePath = Path.Combine(uploads, UploadFiles); - if (System.IO.File.Exists(filePath)) - { - //Delete the file from server - System.IO.File.Delete(filePath); - } - } + statusMessage = "Syncfusion File Uploader has been successfully created and initialized!"; + Console.WriteLine(statusMessage); + // You could also interact with JavaScript to modify DOM here if needed. + // For example: JSRuntime.InvokeVoidAsync("someJsFunction"); } } {% endhighlight %} {% endtabs %} -The [OnFailure](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnFailure) event is triggered when there is a failure in the AJAX request during the uploading or removing of files. It provides a way to handle and respond to any errors or issues that occur during the file upload or removal process. +N> The [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event is useful for client-side JavaScript interop if you need to manipulate the DOM elements of the uploader component immediately after it's ready. However, for most Blazor-specific customizations (like custom templates), you should use the built-in Blazor features. + +### Preview + +{% previewsample "https://blazorplayground.syncfusion.com/embed/VtLyNuVUBGtPZrdo?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} + +![Blazor File Upload Created Example](images/blazor-fileupload-created.gif) + + +## File Selected Event + +The [`FileSelected Event`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_FileSelected)event is triggered when files are chosen from the file explorer dialog, but **before** the [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event. This event provides an opportunity to perform validations on the selected files (e.g., file size, type, count) and decide whether to proceed with the upload/value change or cancel the selection. It's ideal for immediate client-side feedback or preventative actions. + +### Code Example + +This example demonstrates how to use the [FileSelected Event](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_FileSelected) event to prevent files larger than a certain size. {% tabs %} {% highlight razor %} - - - +@using Syncfusion.Blazor.Inputs +@using System.Linq + + + +

@validationMessage

+ @code { - private void OnFailureHandler(FailureEventArgs args) - { - // Here, you can customize your code. - } - private void OnActionCompleteHandler(ActionCompleteEventArgs args) + private string validationMessage = ""; + private readonly long MaxFileSize = 1024 * 1024; // 1 MB + + private void OnFileSelected(SelectedEventArgs args) { - // Here, you can customize your code. + validationMessage = ""; + foreach (var file in args.FilesData) + { + if (file.Size > MaxFileSize) + { + validationMessage += $"Error: File '{file.Name}' exceeds {MaxFileSize / (1024 * 1024)} MB limit. "; + args.Cancel = true; // Prevents this file from being added + } + } + if (!string.IsNullOrEmpty(validationMessage)) + { + Console.WriteLine(validationMessage); + } + else + { + Console.WriteLine("Files selected successfully and passed initial validation."); + } } } {% endhighlight %} {% endtabs %} -## Configure allowed file types +N> Setting `args.Cancel = true` in the `FileSelected` event will prevent the file (or files if `args.Files` contains multiple) from being added to the uploader's internal file list. This is a client-side validation and should be complemented with server-side validation for robust security and data integrity. + +### Preview + +{% previewsample "https://blazorplayground.syncfusion.com/embed/BDLIZuBUVwEJoJpz?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} + +![Blazor File Upload File Selected Example](images/blazor-fileupload-fileselected.gif) + -Restrict uploads to specific file types using the [AllowedExtensions](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderModel.html#Syncfusion_Blazor_Inputs_UploaderModel_AllowedExtensions) property. Provide extensions as a comma-separated list. The Uploader filters selected or dropped files to match the specified types before uploading. For accurate client-side filtering, set the accept attribute on the underlying input element as needed. +## OnFileListRender + +The [`OnFileListRender`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnFileListRender) event allows you to customize individual file list items before they are rendered in the uploader's UI. This is highly useful for scenarios where you need to display additional information alongside each file, such as a custom preview, metadata, or actions. + +### Code Example + +This example demonstrates how to use [`OnFileListRender`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnFileListRender) {% tabs %} {% highlight razor %} - +@using Syncfusion.Blazor.Inputs + + + + + + +@code { + SfUploader fileobj; + private void OnFileListRenderHandler(FileListRenderingEventArgs args) + { + + } +} {% endhighlight %} {% endtabs %} -{% previewsample "https://blazorplayground.syncfusion.com/embed/rXhzDsrOqbKVNviI?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" backgroundimage "[Allowing Specific Files in Blazor FileUpload](./images/blazor-fileupload-allow-specific-file.png)" %} N> [View Sample in GitHub](https://github.com/SyncfusionExamples/Blazor-Getting-Started-Examples/tree/main/FileUpload). @@ -427,4 +546,5 @@ N> [View Sample in GitHub](https://github.com/SyncfusionExamples/Blazor-Getting- 1. [Getting Started with Syncfusion® Blazor for client-side in .NET Core CLI](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-app) 2. [Getting Started with Syncfusion® Blazor for client-side in Visual Studio](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-visual-studio) 3. [Getting Started with Syncfusion® Blazor for server-side in .NET Core CLI](https://blazor.syncfusion.com/documentation/getting-started/blazor-web-app) -4. [Getting Started with Syncfusion® File Upload in Blazor WebAssembly using Visual Studio](https://blazor.syncfusion.com/documentation/file-upload/how-to/getting-started-with-blazor-webassembly) \ No newline at end of file +4. [Getting Started with Syncfusion® File Upload in Blazor WebAssembly using Visual Studio](https://blazor.syncfusion.com/documentation/file-upload/how-to/getting-started-with-blazor-webassembly) +5. [How to convert images to Base64 string with Blazor File Upload](https://support.syncfusion.com/kb/article/21178/how-to-convert-images-to-base64-string-with-blazor-file-upload) \ No newline at end of file diff --git a/blazor/file-upload/getting-started.md b/blazor/file-upload/getting-started.md index f032a963d0..0d973d53db 100644 --- a/blazor/file-upload/getting-started.md +++ b/blazor/file-upload/getting-started.md @@ -7,9 +7,9 @@ control: File Upload documentation: ug --- -# Getting Started with Blazor File Upload Component in WASM App +# Getting Started with Blazor File Upload Component -This guide explains how to add the [Blazor File Upload](https://www.syncfusion.com/blazor-components/blazor-file-upload) component to a Blazor WebAssembly (WASM) app using Visual Studio, Visual Studio Code, and the .NET CLI. It also outlines optional server API patterns that a WASM app can call for storing files. +This section briefly explains how to include the [Blazor File Upload](https://www.syncfusion.com/blazor-components/blazor-file-upload) component in your Blazor WebAssembly App using Visual Studio, Visual Studio Code, and the .NET CLI. {% tabcontents %} @@ -17,15 +17,15 @@ This guide explains how to add the [Blazor File Upload](https://www.syncfusion.c ## Prerequisites -* Review the [system requirements for Blazor components](https://blazor.syncfusion.com/documentation/system-requirements) and ensure the required .NET SDK and tooling are installed. +* [System requirements for Blazor components](https://blazor.syncfusion.com/documentation/system-requirements) ## Create a new Blazor App in Visual Studio -Create a **Blazor WebAssembly App** using Visual Studio via [Microsoft Templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-9.0&pivots=vs) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-integration/template-studio). For detailed instructions, refer to [this guide](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-app). +You can create a **Blazor WebAssembly App** using Visual Studio via [Microsoft Templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-9.0&pivots=vs) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-integration/template-studio). For detailed instructions, refer to [this guide](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-app). -## Install Syncfusion® Blazor Inputs and Themes NuGet in the App +## Install Syncfusion® Blazor Inputs and Themes NuGet in the App -To use the **Blazor File Upload** component, open NuGet Package Manager in Visual Studio (Tools → NuGet Package Manager → Manage NuGet Packages for Solution), then install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/). Alternatively, use the following Package Manager commands. +To add the **Blazor File Upload** component in the app, open the NuGet package manager in Visual Studio (*Tools → NuGet Package Manager → Manage NuGet Packages for Solution*), search and install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/). Alternatively, you can utilize the following package manager command to achieve the same. {% tabs %} {% highlight C# tabtitle="Package Manager" %} @@ -36,7 +36,7 @@ Install-Package Syncfusion.Blazor.Themes -Version {{ site.releaseversion }} {% endhighlight %} {% endtabs %} -N> Syncfusion® Blazor components are hosted on [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). See the [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for a complete package list and component details. +N> Syncfusion® Blazor components are available in [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). Refer to [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for available NuGet packages list with component details. {% endtabcontent %} @@ -44,13 +44,13 @@ N> Syncfusion® Blazor components are hosted ## Prerequisites -* Review the [system requirements for Blazor components](https://blazor.syncfusion.com/documentation/system-requirements) and install the latest .NET SDK. +* [System requirements for Blazor components](https://blazor.syncfusion.com/documentation/system-requirements) ## Create a new Blazor App in Visual Studio Code -Create a **Blazor WebAssembly App** using Visual Studio Code via [Microsoft Templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-7.0&pivots=vsc) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-code-integration/create-project). For step-by-step instructions, see [this guide](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-app?tabcontent=visual-studio-code). +You can create a **Blazor WebAssembly App** using Visual Studio Code via [Microsoft Templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-7.0&pivots=vsc) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-code-integration/create-project). For detailed instructions, refer to [this guide](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-app?tabcontent=visual-studio-code). -Alternatively, create a WebAssembly application using the following command in the terminal (Ctrl+`). +Alternatively, you can create a WebAssembly application using the following command in the terminal(Ctrl+`). {% tabs %} @@ -63,11 +63,11 @@ cd BlazorApp {% endtabs %} -## Install Syncfusion® Blazor Inputs and Themes NuGet in the App +## Install Syncfusion® Blazor Inputs and Themes NuGet in the App -* Press Ctrl+` to open the integrated terminal in Visual Studio Code. -* Ensure the project root directory where your `.csproj` file is located. -* Run the following commands to install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/) and restore dependencies. +* Press Ctrl+` to open the integrated terminal in Visual Studio Code. +* Ensure you’re in the project root directory where your `.csproj` file is located. +* Run the following command to install a [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/) NuGet package and ensure all dependencies are installed. {% tabs %} @@ -81,7 +81,7 @@ dotnet restore {% endtabs %} -N> Syncfusion® Blazor components are hosted on [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). See the [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for a complete package list and component details. +N> Syncfusion® Blazor components are available in [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). Refer to [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for available NuGet packages list with component details. {% endtabcontent %} @@ -89,7 +89,7 @@ N> Syncfusion® Blazor components are hosted ## Prerequisites -Install the latest [.NET Core SDK](https://dotnet.microsoft.com/en-us/download). If already installed, check the version using the following command in a terminal or command prompt. +Latest version of the [.NET Core SDK](https://dotnet.microsoft.com/en-us/download). If you previously installed the SDK, you can determine the installed version by executing the following command in a command prompt (Windows) or terminal (macOS) or command shell (Linux). {% tabs %} {% highlight c# tabtitle=".NET CLI" %} @@ -99,9 +99,9 @@ dotnet --version {% endhighlight %} {% endtabs %} -## Create a Blazor WebAssembly App using .NET CLI +## Create a Blazor WebAssembly project using .NET CLI -Run the `dotnet new blazorwasm` command to create a new Blazor WebAssembly app in a command prompt (Windows), terminal (macOS), or shell (Linux). +Run the `dotnet new blazorwasm` command to create a new Blazor WebAssembly application in a command prompt (Windows) or terminal (macOS) or command shell (Linux). {% tabs %} {% highlight c# tabtitle=".NET CLI" %} @@ -112,11 +112,11 @@ cd BlazorApp {% endhighlight %} {% endtabs %} -This command creates a new Blazor WebAssembly app in a directory named `BlazorApp` at the current location. See the [Create Blazor app](https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/create) and [dotnet new CLI](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-new) topics for details. +This command creates new Blazor WebAssembly app project and places it in a new directory called `BlazorApp` inside your current location. See [Create Blazor app topic](https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/create) and [dotnet new CLI command](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-new) topics for more details. -## Install Syncfusion® Blazor Inputs and Themes NuGet in the App +## Install Syncfusion® Blazor Inputs and Themes NuGet in the App -Install the **Blazor File Upload** component packages using the .NET CLI. Run the following commands to install [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs/) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/). See [Install and manage packages using the dotnet CLI](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-dotnet-cli) for more information. +Here's an example of how to add the **Blazor File Upload** component in the application using the following command in the command prompt (Windows) or terminal (Linux and macOS) to install a [Syncfusion.Blazor.Inputs](https://www.nuget.org/packages/Syncfusion.Blazor.Inputs/) and [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/) NuGet package. See [Install and manage packages using the dotnet CLI](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-dotnet-cli) topics for more details. {% tabs %} {% highlight c# tabtitle=".NET CLI" %} @@ -128,7 +128,7 @@ dotnet restore {% endhighlight %} {% endtabs %} -N> Syncfusion® Blazor components are hosted on [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). See the [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for a complete package list and component details. +N> Syncfusion® Blazor components are available in [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). Refer to [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for available NuGet packages list with component details. {% endtabcontent %} @@ -136,7 +136,7 @@ N> Syncfusion® Blazor components are hosted ## Add Import Namespaces -Open **~/_Imports.razor** and import the `Syncfusion.Blazor` and `Syncfusion.Blazor.Inputs` namespaces. +Open the **~/_Imports.razor** file and import the `Syncfusion.Blazor` and `Syncfusion.Blazor.Inputs` namespace. {% tabs %} {% highlight razor tabtitle="~/_Imports.razor" %} @@ -147,9 +147,9 @@ Open **~/_Imports.razor** and import the `Syncfusion.Blazor` and `Syncfusion.Bla {% endhighlight %} {% endtabs %} -## Register Syncfusion® Blazor Service +## Register Syncfusion® Blazor Service -Register Syncfusion® Blazor services in **~/Program.cs** so the components are available to the app. +Register the Syncfusion® Blazor Service in the **~/Program.cs** file of your Blazor WebAssembly App. {% tabs %} {% highlight C# tabtitle="~/Program.cs" hl_lines="3 11" %} @@ -171,9 +171,9 @@ await builder.Build().RunAsync(); {% endhighlight %} {% endtabs %} -## Add stylesheet and script resources +## Add Stylesheet and Script Resources -The theme stylesheet and script are available via [Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets). Add the references in the `` section of **~/index.html**. +The theme stylesheet and script can be accessed from NuGet through [Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets). Include the stylesheet and script references in the `` section of the **~/index.html** file. ```html @@ -185,11 +185,24 @@ The theme stylesheet and script are available via [Static Web Assets](https://bl ``` -N> See the [Blazor Themes](https://blazor.syncfusion.com/documentation/appearance/themes) topic for ways to reference themes ([Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets), [CDN](https://blazor.syncfusion.com/documentation/appearance/themes#cdn-reference), and [CRG](https://blazor.syncfusion.com/documentation/common/custom-resource-generator)). For scripts, see [Adding Script Reference](https://blazor.syncfusion.com/documentation/common/adding-script-references). When using the consolidated Syncfusion script, separate per-component scripts are typically not required. +N> Check out the [Blazor Themes](https://blazor.syncfusion.com/documentation/appearance/themes) topic to discover various methods ([Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets), [CDN](https://blazor.syncfusion.com/documentation/appearance/themes#cdn-reference), and [CRG](https://blazor.syncfusion.com/documentation/common/custom-resource-generator)) for referencing themes in your Blazor application. Also, check out the [Adding Script Reference](https://blazor.syncfusion.com/documentation/common/adding-script-references) topic to learn different approaches for adding script references in your Blazor application. -## Add Blazor File Upload component +## Add Syncfusion® Blazor File Upload component -Add the Syncfusion® Blazor File Upload component in **~/Pages/Index.razor**. +The Syncfusion Blazor File Upload component allows you to seamlessly integrate file upload functionalities into your Blazor applications. It supports various features like asynchronous and synchronous uploads, file type validation, progress tracking, and custom templates. A common use case is enabling users to upload documents, images, or other files to a server, or process them directly within the client-side application. + +## Simple Code to render a Usable File Upload Component + +The most basic way to render the File Upload component is by adding the `` tag to your `.razor` page. By default, this component provides a clean interface for users to select files locally. + +Add the Syncfusion® Blazor File Upload component in the **~/Components/Pages/Home.razor** file. If an interactivity location as `per page/component`, define a render mode at the top of the `Home.razor` page. + +N> If an Interactivity Location is set to `Global` and the **Render Mode** is set to `Server`, the render mode is configured in the `App.razor` file by default. + +``` +@* desired render mode define here *@ +@rendermode InteractiveServer +``` {% tabs %} {% highlight razor %} @@ -199,41 +212,62 @@ Add the Syncfusion® Blazor File Upload comp {% endhighlight %} {% endtabs %} -* Press Ctrl+F5 (Windows) or +F5 (macOS) to run the application. The Syncfusion® Blazor File Upload component renders in the default browser. +### Preview + +* Press Ctrl+F5 (Windows) or +F5 (macOS) to launch the application. This will render the Syncfusion® Blazor File Upload component in your default web browser. -{% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" backgroundimage "[Blazor FileUpload Component](./images/blazor-fileupload-component.png)" %} +{% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -## Without server-side API endpoint +![Blazor File Upload Basic Component](images/blazor-fileupload-basic.gif) -In a Blazor WebAssembly app, files can be selected and processed on the client without specifying a server-side API endpoint using [AsyncSettings](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderAsyncSettings.html). Client apps cannot write arbitrary files to the device file system. For persistence, use browser storage (such as IndexedDB) or upload to a server API. +## Use ValueChange Event -### Save and Remove actions +The [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event fires when files are selected or removed from the uploader. This event is crucial for client-side processing of selected files, allowing you to access file details and their content, which is useful for previewing files or handling uploads without a server-side endpoint. -The [ValueChange](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event exposes uploaded files as streams. The example below demonstrates a save handler pattern. In WebAssembly, adapt this logic to save to browser storage or to POST streams to a backend API. Avoid using unbounded stream limits in production. +### Code Example + +This example demonstrates how to use the [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event to save uploaded files directly to a local directory when the [`AutoUpload`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.SfUploader.html#Syncfusion_Blazor_Inputs_SfUploader_AutoUpload) property is set to `true`. This is useful for scenarios where you want to process files immediately after selection without an explicit upload button. {% tabs %} -{% highlight cshtml %} +{% highlight razor %} @using Syncfusion.Blazor.Inputs +@using System.IO + + @code { private async Task OnChange(UploadChangeEventArgs args) { try { - foreach (var file in args.Files) + foreach (var fileEntry in args.Files) { - var path = @"" + file.FileInfo.Name; - FileStream filestream = new FileStream(path, FileMode.Create, FileAccess.Write); - await file.File.OpenReadStream(long.MaxValue).CopyToAsync(filestream); - filestream.Close(); + // Define a path where you want to save the file. + // For a Blazor Server app, this path will be on the server. + var uploadsFolder = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads"); + if (!Directory.Exists(uploadsFolder)) + { + Directory.CreateDirectory(uploadsFolder); + } + + // Construct the full file path. + var filePath = Path.Combine(uploadsFolder, fileEntry.FileInfo.Name); + + // Use a FileStream to write the uploaded file's content to the server. + await using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) + { + // OpenReadStream with long.MaxValue allows reading the entire stream. + await fileEntry.File.OpenReadStream(long.MaxValue).CopyToAsync(fileStream); + } + Console.WriteLine($"File '{fileEntry.FileInfo.Name}' saved successfully to '{filePath}'"); } } catch (Exception ex) { - Console.WriteLine(ex.Message); + Console.WriteLine($"Error saving file: {ex.Message}"); } } } @@ -241,85 +275,72 @@ The [ValueChange](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs {% endhighlight %} {% endtabs %} -![Blazor FileUpload displays Updated Files](./images/blazor-fileupload-with-updated-files.png) +N> When saving files directly in a Blazor Server application using [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) and [`AutoUpload`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.SfUploader.html#Syncfusion_Blazor_Inputs_SfUploader_AutoUpload), the files are saved on the server where the Blazor Server app is running, not on the client's machine. You need appropriate file system permissions for the server process to write to the specified directory. Also, ensure the target directory (`wwwroot/uploads` in this example) exists or is created programmatically. In a production environment, consider secure storage solutions for uploaded files. -When the remove icon is selected in the file list, the [OnRemove](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnRemove) event provides the file name to remove. Implement the remove logic appropriate to your storage choice (for example, removing from IndexedDB or calling a backend API). The following sample shows the event signature pattern. +### Preview -{% tabs %} -{% highlight cshtml %} +{% previewsample "https://blazorplayground.syncfusion.com/embed/hDVyZkrqBvaSlvht?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -Private void onRemove(RemovingEventArgs args) -{ - foreach(var removeFile in args.FilesData) - { - if (File.Exists(Path.Combine(@"rootPath", removeFile.Name))) - { - File.Delete(Path.Combine(@"rootPath", removeFile.Name)) - } - } -} +![Blazor File Upload ValueChange Event](images/blazor-fileupload-valuechange.gif) -{% endhighlight %} -{% endtabs %} -## With server-side API endpoint +## Memory stream -The upload process requires save and remove action URL to manage the upload process in the server. +When you need to process uploaded files in memory—perhaps for resizing images, reading content, or sending them to another service without saving them to disk first—using a `MemoryStream` is an efficient approach. This is particularly useful for temporary processing or when dealing with sensitive data that shouldn't persist on the file system. -N> * The save action is necessary to handle the upload operation. -
* The remove action is optional, one can handle the removed files from server. +### Code Example -The save action handler upload the files that needs to be specified in the [SaveUrl](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderAsyncSettings.html#Syncfusion_Blazor_Inputs_UploaderAsyncSettings_SaveUrl) property. +This example demonstrates how to read the content of an uploaded file into a [MemoryStream Class](https://learn.microsoft.com/en-us/dotnet/api/system.io.memorystream)`. This allows you to perform in-memory operations on the file, such as converting an image to a Base64 string, without requiring disk I/O. -The save handler receives the submitted files and manages the save process in server. After uploading the files to server location, the color of the selected file name changes to green and the remove icon is changed as bin icon. +{% tabs %} +{% highlight razor %} -Set the [RemoveUrl](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderAsyncSettings.html#Syncfusion_Blazor_Inputs_UploaderAsyncSettings_RemoveUrl) property to the server endpoint that deletes files. The [OnActionComplete](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnActionComplete) event triggers after all selected files are processed (success or failure). +@using Syncfusion.Blazor.Inputs +@using System.IO -{% tabs %} -{% highlight cshtml %} + + + -[Route("api/[controller]")] -public class SampleDataController : Controller +@if (!string.IsNullOrEmpty(base64Image)) { - public string uploads = ".\\Uploaded Files"; // replace with your directory path +

Uploaded Image Preview (Base64)

+ +} + +@code { + private string base64Image; - [HttpPost("[action]")] - public async Task Save(IFormFile UploadFiles) // Save the uploaded file here + private async Task OnValueChangeMemoryStream(UploadChangeEventArgs args) { - if (UploadFiles.Length > 0) - { - //Create directory if not exists - if (!Directory.Exists(uploads)) - { - Directory.CreateDirectory(uploads); - } + base64Image = string.Empty; // Clear previous image - var filePath = Path.Combine(uploads, UploadFiles.FileName); - if (System.IO.File.Exists(filePath)) - { - //Return conflict status code - return new StatusCodeResult(StatusCodes.Status409Conflict); - } - using (var fileStream = new FileStream(filePath, FileMode.Create)) + foreach (var fileEntry in args.Files) + { + if (fileEntry.FileInfo.Type.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) { - //Save the uploaded file to server - await UploadFiles.CopyToAsync(fileStream); + // Create a MemoryStream to hold the file content. + using var memoryStream = new MemoryStream(); + + // Copy the file's content from the upload stream to the MemoryStream. + await fileEntry.File.OpenReadStream(long.MaxValue).CopyToAsync(memoryStream); + + // Convert the MemoryStream content to a byte array. + byte[] imageBytes = memoryStream.ToArray(); + + // Convert byte array to Base64 string for display or further processing. + base64Image = Convert.ToBase64String(imageBytes); + Console.WriteLine($"Image '{fileEntry.FileInfo.Name}' loaded into MemoryStream and converted to Base64."); } - } - return Ok(); - } - - - [HttpPost("[action]")] - public void Remove(string UploadFiles) // Delete the uploaded file here - { - if(UploadFiles != null) - { - var filePath = Path.Combine(uploads, UploadFiles); - if (System.IO.File.Exists(filePath)) + else { - //Delete the file from server - System.IO.File.Delete(filePath); + Console.WriteLine($"File '{fileEntry.FileInfo.Name}' is not an image and won't be processed for Base64 preview."); + // For non-image files, you could read their content as text or handle differently. + // Example for text file: + // memoryStream.Position = 0; // Reset stream position + // using var reader = new StreamReader(memoryStream); + // var content = await reader.ReadToEndAsync(); + // Console.WriteLine($"Content of {fileEntry.FileInfo.Name}: {content}"); } } } @@ -328,181 +349,154 @@ public class SampleDataController : Controller {% endhighlight %} {% endtabs %} -The [OnFailure](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnFailure) event is raised when an AJAX request fails during upload or removal. Use it to surface and handle server-side errors in the UI. +N> When using `MemoryStream` for large files, be mindful of server memory consumption. If you expect very large files, consider processing them in chunks or saving them to temporary storage before processing to avoid out-of-memory exceptions. The `long.MaxValue` in `OpenReadStream` indicates the maximum buffer size. In a Blazor Server app, `Stream` operations occur on the server. -{% tabs %} -{% highlight razor %} +#### Preview - - - - -@code { - private void OnFailureHandler(FailureEventArgs args) - { - // Here, you can customize your code. - } - private void OnActionCompleteHandler(ActionCompleteEventArgs args) - { - // Here, you can customize your code. - } -} +{% previewsample "https://blazorplayground.syncfusion.com/embed/BjreNaLUhdwxzvHS?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -{% endhighlight %} -{% endtabs %} +#### Gif -### Server-side configuration for saving and returning responses +![Blazor File Upload Memory Stream Example](images/blazor-fileupload-memorystream.gif) -The following sample demonstrates a server action that saves files and returns responses in JSON, string, and file formats. In production, validate file type and size, sanitize file names, use unique file naming, and write to a dedicated uploads directory. -{% tabs %} -{% highlight cshtml %} +## Created Event -[Route("api/[controller]")] +The [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event fires after the File Upload component has been rendered and initialized. This event is a good place to perform any initial setup, attach custom event listeners to the component's DOM elements, or apply custom styling that requires the component to be fully rendered. -private IHostingEnvironment hostingEnv; +### Code Example -public SampleDataController(IHostingEnvironment env) -{ - this.hostingEnv = env; -} +This example shows how to use the [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event to add a custom message or dynamically change some aspect of the uploader's appearance right after it's created. -[HttpPost("[action]")] -public IActionResult Save() -{ - // for JSON Data - try - { - // Process uploaded files - var responseData = new - { - Success = true, - Message = "Files uploaded successfully", - // Additional data can be added here - }; +{% tabs %} +{% highlight razor %} - return Ok(responseData); - } - catch (Exception e) - { - var errorResponse = new - { - Success = false, - Message = "File upload failed: " + e.Message - }; +@using Syncfusion.Blazor.Inputs - return BadRequest(errorResponse); - } + + + - // for String Data - try - { - // Process string data - var data = "success"; - // Return the string data - return Content(data); - } - catch (Exception) - { - var data = "failed"; - return Content(data); - } +

@statusMessage

- // for File Data - try - { - // Example: Retrieve file path for stream.txt - var filePath = "stream.txt"; // Example file path - - var fullPath = Path.GetFullPath(filePath); +@code { + private string statusMessage = "Uploader not yet created."; - // Return the file - return PhysicalFile(fullPath, "text/plain"); - } - catch (Exception e) + private void OnUploaderCreated() { - return Content("Failed to retrieve file response: " + e.Message, "text/plain"); + statusMessage = "Syncfusion File Uploader has been successfully created and initialized!"; + Console.WriteLine(statusMessage); + // You could also interact with JavaScript to modify DOM here if needed. + // For example: JSRuntime.InvokeVoidAsync("someJsFunction"); } - } {% endhighlight %} {% endtabs %} -### Client-side configuration for saving and returning responses +N> The [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event is useful for client-side JavaScript interop if you need to manipulate the DOM elements of the uploader component immediately after it's ready. However, for most Blazor-specific customizations (like custom templates), you should use the built-in Blazor features. + +### Preview + +{% previewsample "https://blazorplayground.syncfusion.com/embed/VtLyNuVUBGtPZrdo?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -The following sample shows how to handle success responses on the client and interpret JSON/string/file responses from the server. Adapt the logic to your API contract. +![Blazor File Upload Created Example](images/blazor-fileupload-created.gif) + + +## File Selected Event + +The [`FileSelected Event`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_FileSelected)event is triggered when files are chosen from the file explorer dialog, but **before** the [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event. This event provides an opportunity to perform validations on the selected files (e.g., file size, type, count) and decide whether to proceed with the upload/value change or cancel the selection. It's ideal for immediate client-side feedback or preventative actions. + +### Code Example + +This example demonstrates how to use the [FileSelected Event](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_FileSelected) event to prevent files larger than a certain size. {% tabs %} -{% highlight cshtml %} +{% highlight razor %} @using Syncfusion.Blazor.Inputs -@using System.Text.Json - +@using System.Linq - - - + + +

@validationMessage

@code { + private string validationMessage = ""; + private readonly long MaxFileSize = 1024 * 1024; // 1 MB - private void OnSuccessHandler(SuccessEventArgs args) + private void OnFileSelected(SelectedEventArgs args) { - if (args.Response is not null) // Check if the event argument is not null + validationMessage = ""; + foreach (var file in args.FilesData) { - var responseText = args.Response.ResponseText; - if (!string.IsNullOrWhiteSpace(responseText)) - { - // for JSON and File Datas - using var jsonDoc = JsonDocument.Parse(responseText); - var jsonResponse = jsonDoc.RootElement; - - if (jsonResponse.TryGetProperty("success", out var successProp)) - { - var isSuccess = successProp.GetBoolean(); - - if (isSuccess) - { - // File upload success - var message = jsonResponse.TryGetProperty("message", out var messageProp) ? messageProp.GetString() : "File uploaded successfully"; - - // Additional processing as needed - } - } - - - // for string Data - var message = responseText; - // Additional processing as needed - } + if (file.Size > MaxFileSize) + { + validationMessage += $"Error: File '{file.Name}' exceeds {MaxFileSize / (1024 * 1024)} MB limit. "; + args.Cancel = true; // Prevents this file from being added + } + } + if (!string.IsNullOrEmpty(validationMessage)) + { + Console.WriteLine(validationMessage); + } + else + { + Console.WriteLine("Files selected successfully and passed initial validation."); } } - } {% endhighlight %} {% endtabs %} -## Configure allowed file types +N> Setting `args.Cancel = true` in the `FileSelected` event will prevent the file (or files if `args.Files` contains multiple) from being added to the uploader's internal file list. This is a client-side validation and should be complemented with server-side validation for robust security and data integrity. + +### Preview -Restrict uploads to specific file types using the [AllowedExtensions](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderModel.html#Syncfusion_Blazor_Inputs_UploaderModel_AllowedExtensions) property. Provide extensions as a comma-separated list, typically with a leading dot for each extension. The Uploader filters selected or dropped files to match the specified types and then processes the upload operation. For client-side filtering, ensure the accept attribute of the underlying input reflects the same extensions. +{% previewsample "https://blazorplayground.syncfusion.com/embed/BDLIZuBUVwEJoJpz?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} + +![Blazor File Upload File Selected Example](images/blazor-fileupload-fileselected.gif) + + +## OnFileListRender + +The [`OnFileListRender`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnFileListRender) event allows you to customize individual file list items before they are rendered in the uploader's UI. This is highly useful for scenarios where you need to display additional information alongside each file, such as a custom preview, metadata, or actions. + +### Code Example + +This example demonstrates how to use [`OnFileListRender`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_OnFileListRender) {% tabs %} {% highlight razor %} - +@using Syncfusion.Blazor.Inputs + + + + + + +@code { + SfUploader fileobj; + private void OnFileListRenderHandler(FileListRenderingEventArgs args) + { + + } +} {% endhighlight %} {% endtabs %} -{% previewsample "https://blazorplayground.syncfusion.com/embed/rXhzDsrOqbKVNviI?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" backgroundimage "[Allowing Specific Files in Blazor FileUpload](./images/blazor-fileupload-allow-specific-file.png)" %} N> [View Sample in GitHub](https://github.com/SyncfusionExamples/Blazor-Getting-Started-Examples/tree/main/FileUpload). + ## See also * [Getting Started with Syncfusion® Blazor for Client-Side in .NET Core CLI](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-app) * [Getting Started with Syncfusion® Blazor for Server-side in Visual Studio](https://blazor.syncfusion.com/documentation/getting-started/blazor-server-side-visual-studio) * [Getting Started with Syncfusion® Blazor for Server-Side in .NET Core CLI](https://blazor.syncfusion.com/documentation/getting-started/blazor-web-app) -* [Getting Started with Syncfusion® File Upload in Blazor WebAssembly using Visual Studio](https://blazor.syncfusion.com/documentation/file-upload/how-to/getting-started-with-blazor-webassembly) \ No newline at end of file +* [Getting Started with Syncfusion® File Upload in Blazor WebAssembly using Visual Studio](https://blazor.syncfusion.com/documentation/file-upload/how-to/getting-started-with-blazor-webassembly) +* [How to convert images to Base64 string with Blazor File Upload](https://support.syncfusion.com/kb/article/21178/how-to-convert-images-to-base64-string-with-blazor-file-upload) diff --git a/blazor/file-upload/images/blazor-fileupload-basic.gif b/blazor/file-upload/images/blazor-fileupload-basic.gif new file mode 100644 index 0000000000000000000000000000000000000000..68ffe86d171e37b0c794c82de31321d5774eceb4 GIT binary patch literal 62010 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 literal 0 HcmV?d00001 diff --git a/blazor/file-upload/images/blazor-fileupload-fileselected.gif b/blazor/file-upload/images/blazor-fileupload-fileselected.gif new file mode 100644 index 0000000000000000000000000000000000000000..8617f4b8674f2d9859abfe729ab0c53fcc3a5754 GIT binary patch literal 167216 zcmeEv2Ut^CyKd-62LWl)RRly(QB-sQktzscLlNm66k!li5RfLl7Xj(LBfa%ktQbGh$7l~zWlE|JSQ{W|6;GlVMmhv7e^D`#i zyCm$-*;${HbE?y_YcO$W(DLZebLnt$sGa7~5=mz71zhwcB+O>543z9 z>-eb|IqR8+8C!mSVHNq_CdR|!lk?jM*Eiwb<{=-fqu$s$TiC_DwU2jj^KIdV*xuRMJ#2g1+dF%E z`+NIG?d$&6qxS9Vx1)A&*uH^h7}Wm#Z-DK9SpN2XrCXv3w=eMV<6wa^u- zo9xGJxT-hWl*`k9OK{p>zZ;*FfnU2J7P;zZeBzvHPD#Mtsr10&mne-D`x#eSf#+xo z`gi@!G(3;74MkHQHIJQtJU!VuT-!X%^SsSSpxW_bP`gT7BE4MEsgoIrrL^pwvB#*n zGc}t%Pa?dKzge2hws|mmp~mv85BlU(wqK0Zy`EAX+bcjLs$Y!L>NB3&m_K_)ZJ@2R zc#BAs_u~P}-LjIMwYlz;=j~-*tw{Lv8X^R4m3`e~vvrZIN%)QBw`lkDMsPzrDm|e^F5BXw(-NpXt|_ID2)RonQ~_f#%0z^|tfFUrV^)yW;E2U;8R=lA*eC=)yXWXIggk%o~@;r1|(|QZ+1+^D3E@i2+ZV=93b$C1F^G z*+mN}8Ds|)24qQn28))Nh-4lt><+8NjD&O|djjUniYh{eB_i95ipy^i$&?BrSOh)G zGBDUpp_DZLz9(+TJowN+TK1rmVftJ0*p%g>rJEEYg|l^`s>SQcd+8;Cv@F2a9EvrR~yL8mrP6W zV^dd~cp24J8xeWDYfW_3M$6bjv#D!s#B`A#TCc3}UTQnGPjac?7WuRFPN_4kYh8B? z7uUNbBu7)RAM2)V^bs*Bom*e2WeYliJPOF~Z%+H4Hz}M7xuw4|6K=?S zbvCMh`|5mLyX7@R(%Z7Di)q6R6-zn&Cf9KYD?TG4@>V_d{IRewl7@$mku+);J`4|r z3;O{6qoQT_T-QWR*F;_4RNcT-)5y%w{H?Kt&C54-A3gm7KSf}~^3R4#qTtVNFFH?B z5j3fWdw9Y_(WQnFz_7u?7VMaqoQaK1qWZ_i0~qgnf@?s~v0%=JXVJle31MmHVqbcT zLoJAN-4n;M3zzmf?ui?Cw8Ho&gb8Vdi8(ZhPid2}Yf!MMAJ?vP2*nPw5$+(z`rjV0gmF=EA6jV4_oIVq!W88ml%l-D4IO7S@}dY^Tn#(>&myeaOLT z#9>~}NukJj{N8C2($mM4&QL!%!>P(erNpI>z)h~eO+n2=c9)m@E-!^5uX4~iho1AK z@&crC0%Rf=h@=Gxr3Hy(FN;}Uwy(VGJp?qg&@pMDW3oa#yg~*oSFvDMKhFT=EzHOy za`L2zkerx=npjltZH)*CNd-w+c^P@fJL+%bnH3dTSf2>Lda7jl^r`AoO@pU;Sfj*e^SOUQ0XRD-3wUr2F~OfAe#Elx_yE=nto z&&V#x$|=t-D$CKq$;qqC)iupE56La4$}6nSJ2=RXbuX|KE-0=mnB6aQHYutsDlTv; zE^R38LX=iCl~=WvS2tE198}hZRrx)wuI{L*@2qL=tIKbx4}8(+Dcju9-O@4ChS=?l zH0oU5?dq)V>YwWE?d|L9?_2rScW}@j{;WSubs*n=peJo$Xm-%aVsLP9*yq~F1acHU zJr-{`K8=RMYbR&dre@}*=GJG{z9EWS5C`98=Qrn;Hs+8!i|GBOmAs{uuSnD!20FBc zEH9(bX!J@B$I2Q23R@d378{FATSsm0+cp3Txq5s1n*b~TnDDJd;@j8ZZwH0n4!RCH zqYj3DKNt%he3h?2?it>e-jJ>NH8Fc0cniCYYa#*ASeYv zslP)h+#gmnIIs^-)pS)g4W4Tms_Pi*7`)IiGBbMh+Qi(_^tH9w8=F7ZG2dTtiC?01Fy?utKs2d;b2`{`ETtTL(8=C(ln2@nMni;Ey=R^r&z|_yVQ$ zvA-X69IUGTJnEf;yWgis|4)&hq7stRzoevpIXsFBOL%{pe$C+MsKc?-fMYO6VOC=r zdM3udzcn$pGXCBArN!GJCf5s(4rxkKi zp5oM{pdw}U{9*V<1Hn05kAbsd<2rb~!TUbWqbnZ!YcsxsS5oun(GG`0qJxowAyNLd z10iMyB>5B%w5o$vb7Hv+E`szTTWe9U(=9GSKZiH{RThZ zB|*#s#(P8v|BNsGfua7{=0oqFe|_}f<|8isXGb5N9K%)P!M<2u>j}krLP5|_5cL1cAZUn{g4P(&8UtEmKx+(Wjqz)142YG2GzO$G zeyTA*S|R&lm z9r`5xC`mavh11grkY5s{csdfJLrIdlgzfV^q8@XU5sV+^1YTbo`M)s^c>6NQ!aVei zS;#x9hqeyLfBoWG{~(Z;xPq zzi|5x2_GC1?LVe|c8UuOhztpiiuj!9=$Yy4o#o;G#m_%GEVeu*Atf#;EjBSPHRDTs zYH@Z{L4HzML2`L|R&I7~QFKamLRNipc0*}-O?Gi>c|~n$Wm8RUb8CBVL+ema-%#J+ z=)lnU@YrO}=u+?4QrF1R!1xj-PZp-nj8A}$7@tH8z?UYbW~XLApP!jsn4Vplo<+`p zKDUUNTbNxyFCtf#P;1L8Ye@9Q^2!DZ^i}Y@iCWo0t!|=Lx6rE_=+(`Qt*y<&v{|bg z+Z$Wk>zg|pn>*`UI~!X&n_J*7yPMnFTie^4+dEsJ?|{C$wF~+lcmvXGfkay%Mdvnn zzYF>vc>mWg@t@uh9N3<*KXd9NqqECLSGSL}p6+h69yI;|fkD9`LDbX%)J#_%!eV0M zV(%#`D#%>9l%A29mHj0rH=VzL|H6gB(vqUmi&fP%7rzL~JZx%iX_1qVla!XaCf0kc ze_%kYZ)k8_YIJP)>eWG^0ii34LRYT{q0uPR>XoavZwT)SU%$S8QwE#p{wk;I1>RPw zp~oP1CFx0Bc9}4#tms;|Tze7Z8>M8{j{FFnpu?fsZ5+n4&Z*ygJh zjYof~Zh3qz!<6H2{)>YsUv|-;8p34D=h0Es`RB(=ZNBP|-l5}HzxJ5?;P;g($EkWx zQA^fbdpzRfoT~=Radf7-?Gd8RH{B69F5QWWy55zl8thE08T#-Egx-p@PM;|eU`{*z;VerNLfr?FM!=i^i# zYe?yY-2SXj6ufpUUe(Vbtua0{E|wmlYZ3S{;RXH8W4e~>Q9iIx<8Q}oKjYEPi{N-k zEZE|>^6dusI^Y|ys`*(g%*O^?7-C@{dbg_|M@D2dpZuZD@)>r#6+?33hJe~)TEPWd z?z91VCyDe5iAb)Dsjo!5=?o;YsmTrkFsyV6;YFkD_g%^?;Y3^&B6)GIQeZ@PGV?#(6zl{Z9q|hCwc?kac*M#t>%O)X>sGhy63{iQe3M;mqK{6^Y*;Y4X3%n} zSwFLya2h19JYd!{Nyh&sc&1}9zKByz%5RqEx!qw>R=h*!ZEPT4X0Qu~6Whe-%)}^S zVhk|}pPinZor2FzPR>t(KD97CF*iN6FgrarJ3T);Gmn^FL?D*tKwm)3FQAaisHH{p z0+=e6(8y)!E3cUt0?13D6=oL&uqgT-@t1Fnk2J-fj$sB152>@HWts!&Ec?%;nv#5cU;k8d2^IKga>wE4SeiArp6`D9cG(E37RK?VK8N}Wa zx_=P;Sj6@1+Dpoq%N|*@=Ieyy)7(BJ(wi3wn>1tsiXSK2^ZUlO+^)ju9-x^ui1R zIXAVifLK~wTwGdOoS$D_SU`byK*E+$paH>JTSl#etd+~?^+UPB$kHmNgMaHAKwh@C zx3(~X@`s2VrM(6@uaA;gV)9u2XUV`nsGubm;brh@h<(*)bSMM~Ji zLU`t5;c(^pxg3V89%21xq1RSc&8Tfej#m5L0ZfVC+SbujiPOQ!O;3r&)!oO8i{3ZT zgNx8RC`?|7C^S4;IwC42L0pLMX{BRaZC-st zT2pgEYg@DuT}|g}CDPtLXD))FVMC=cc=qV{bnw(%;K1ynx)S3u%8y}f{mJCk_G2a5 zz5Qr7k)9r_s0cp!u5=fKeiJZ>flYsJ zxY`sA(kZ1vi><{6YFwUL`k(cZ(~TC(Uh=k^8;@J2qu!woDk5J>{qUP4n#-g;!Tcu8 zNUL&qrsW;J(Du0;t5%cZar8DGTgEVI`y3lefDj++9hgQQ;yKb2{E#pTGX%^?G52b0 zW^r`sY1*n3#Qc|Lfk@Z-^9i*=ye)F=#jZY1C)jW8%%!^o-%G`~1S9 z4e~Q8VMTAvdSi3TVn=sx|7-Am@o{v6$W=U2W^;dq-YNmolQ+X?OdG@nXwIo#@}uml zlRNpFeG30z9bUFsn2@stC(Fy;GPJnEj5MvE7)<0paAyW<{ z7?5E6L@*$Y0SN{q7(W#ZVB^wF@LL7N#l_CmNY|>R$|_8&si^@= z?Xj^jVECMzoJ7pd&dtwbIL4KgKbA7T@Pa1_?}Bk&FTfP9B%0zZ0i~9=;-L|?Cb)qx38zC=dg-UU*Evsz`y{g8MH7zzW{0rg1!j)BA{9U zmj$MeUzjxhja~4yj)*F(xR}*om1~Zzv%Z|xuhP9`5muung=%EBej&J24tB68)`C&j z%&Ze02A6XZQAV_Vf>W;2Rt&|0yiu_UFi$=$MGO z_#}JLKiFt!MuBD&Xh!+Ti~OiCpMC$yM)PW!v1i>H(_BROjmkSv!U^|_F zYT55K-0wBo?=_CF?tc{AbT4w$B(d*HP7a_z011+pf7A*99a;eN62|8bw9>)gk98qo zD`TlE;~5*%`TGl?7w#{XeE!fBQqZ1I++G}qC`*|rZX5Qgp9*hTj&GibZ9l+#y8ZoV@3A|})Y-(<5!uUitx3sok8sJzkEDP{yI%HZfPjy{G?SNka>`G^6M`u@e zH{e*hySsb34_OxALj4!ZGCVW{7~09oiu!JN+r;wFEE+yF4LFyjr6m+<8Tfc^V%pZq z)-G@X2fk{+m-Em^4LJRPV#^rUpKo9HF)p0{Enb`iF}C6&R|%L%NC-)%jwN8KYD7xUl1V4RXM(T=U46=a40%2T?#G z0HI*+o3FhFd%Z>>@pWN|^`Da)it5@+8#*hRdn#Ibt6KXSdqx_1Mw|NJ&HWP{!&4o@ z(_N#p-D7jzV^7WyU^dncFrCYSoBko{B3{WGY6>E*#0)F5JIXm({7^tsjH zxwVmb(AP)j*GCrCM=^b4ba7*BVPgzDFK&)4ZH+H&O-#V2rlzK+r)OqnW@l$7W|t;s zm*8{D@Hy1P!pig_8osnSvAi>h-kn<6pI-e6igf>ls{SXwAxN@?+2h$CdF{Hnd;I0K z3&IgT1>nMPLL;N1K{x`qZ$hw>F!znirA!cx5SpD=n8sgJT2>A#sHhIUa^)&BOCt*( z7af-fkg6VGu71w`esC>&5s{G)klmcx5NTtkrKF>yrlshJ zMOv~}+T&Vn+D`AORbpp3Q*aNQ_SeDgr18pe8a>)6z0Cv-0!uN=u6etK7z0Lnj)X_eWjlI=pj=DsqZ}J+q>+ zy0)RA9;jcSe;b-X13I|5wH2d%+uPeZI@&vdd84zlyBlNC0Pf{Iy?y_dq4Qr&tN7tZ z28tuEZ*HZwf9+vhZkPW~K4ZApaq_k)YVW{SIniB?syyc zIR}L+Q-9W=jEeQ35Pw4;K?uVpRXZJ*{Y8)J&O6o93}HE7%B!e+ekQA`uKpPbjGPPy zpH7*93sxe|%1+ZoZEI1!3 z79A{@5SDf>_NBKt)PgwIJ#j3%aA~jOp16TWD~x|an2=VOm_w8Jlr|Z=1_hh?aqT*4 z#>>>q&uN&R(Q-VcRWG7rd`i#ol-}hL1H%(WHWx-M1QVSy6BE-(&{(yZ=^nGNu(00r zWIJ_^o#p`t?L!V$BM$R&P6|cN!K`BTuAi?;%U_j;!8Avc7!8j6(Lvsce;eRk^K!^&G4@f@# zt~l@@L3Bp5#t3`k=@f&mEzBp48D3$eDp#@Zgn6wn_QZNi4x0++&?>MCp;ap2C* zwu5_Aet{8eB?S=&n62%dz1Rc}UbKx!FQV^zoP{yMNIBP?RR(`Di{Nd7k2#cOkh`RMhCB?W+Zt`Sk92!m$8yPa;HhDK*Y!Rg{FHyT%Qg` z2a-bL9!4`DrbcI{M`vf?^K;XS^D|2eGswl6r9}j439*b^SXn`>t}L&uqSjZJ*Vj-R zYpAs~^v3$?#>VQ_1}Fftwz;{swYj#vwZ6Rt$~Ay`FB?1Cn>#z0!Y`oS%kIwR?#|Z1 z{st)AunYR`_TCOgjx*|P!T4yGeqlCFa&1G^=dhxRHJfV?SR(tU}G#87NaGhO%0+&$FKKki1z9{DQ zDy0qaO=boL3gSD~cFZtLG{g4*F3%rA5CmYfhdBW-KT}~*88)`~pWi&>;sCigKrRk{ zb8&#cG6a?(und7^2rNTj`B%X*1f?J-1wknYN^wkgM;AK%s&$!6ac+hna9{ z+j?7iN7@EPI|s+QM&MneQ$3>-J)@I7V^h83Q(a@zJ>#Iy^iRz6!V&!wh`z~%{;9=* z>7~J$r6I)9@a*#F-15jAYIqJkGKU_W2YqFH0X?>`GQPMrzO+8Rv@yQ42}jIM&d*HD zAtx4)@Woa5;@Sjq6}|+X*WpVW6G+gvCYC{#o2^OI)&y#M0<|-V+MdKb?;_Bvphnsh z$bN&~n_AhMUIEYh)0j*+hskg-`EW2PaS-eO^RnU)xPweMAMCkKU%7eny4%eQA3t8d z;LGRh6?nt##tpCQH^l=ZqawqFg&iDDb8wy|A_1vvi13KBsHpI8amYwKy~Kr?=$V+< z_|Edtutn0e)G~3MsOAg2P*a^S0Am^)869)L!sZ*FohFqFk zM$RuUFQSo%74-57pv~4$fHPZNMFY-keHFa{n6nMQoc+O>ZEb-!>syTK?;{5@9UU{XSU{i{UkIO|y<K>i$1GCEH{NU8w z@XX@K%)-zNa%>(ozOXjF2&RtBiMd7i9CB(2HM5L@FRV-=*T8f!g~H4R)978q+PB&D zpZ_j+x72z>`eMWUlHz-2%Fu#}Jt|*JZ$Ax`zv$4^EEal|iM-L4pN@{q&JJeB#ib$O zrjb1$&wd&oUq>wV-pAuGfwGZ2w(o@U4RFVk|8d7Rv~>0kPau}oF~oBc7Z%PTt%-Xm zIN~rCupbW0p|+u=y?da0V6=aDtao^_e*``N3Pi#YV^ecu(+lH>B{*Ucj#!$ULr%{x z&n%*6k*jmdYjddeU%bWe*wk=E6|efX9+&ntqi|AWa^xqe^tq%^&0kih;S8f-aQ&g= zCKVW!7#NoWIC)1GC|Nml*oFs(MutX4!EZP;GBz?gK0FEswa>xN2__6s9|SQsKMTs4 z&o9o+fl?v!UHr*;06_kIsCd^|Sq}?y8tBj?%1m$?y$!}K2l+p~qHq_p zdr3%WRrp7-)W4_A@e;#)+edfFpWpU}mew|qD-YajJ^kQT8w2;+==j7Kd~yN~hfhpS zPJ?02&thh_qr#eK+~ep%MDknzxU`2C)J&PgMPYXQ;(y$l&`JqfDM2eGXr%WBlPc!2**4kP@@JGYo4t2=D%o()}@OFu9^5wX!3vx;vw$JH4hS zqoy~rwl}N3KfAvFOGAHF{XljD=!0JxK_AL#9L#AN%4r(TZyCyK8OduM%WoYkY#S?V zA1i7fEAALCY=;+ifId;&IZ@ID`gBP*=rg4~pd-q9L7y$_n=S8~E$f{t>zgYFy??%< zW4N+oq`G^&rhB}3c%*R%UIG3%uuzHVit>OqIE*2CADr?*;WwwhT{E zK}lvN7G@5f6HH9(JRGMKl|VLY7J5o@a&}50S^}A->-40gWTccmgxxXbE`CDMpc7kx-wx>S<$z_;_8nj6f~7WQzE>w`81OdC+q%TuEAL%IxtULyj$c)}Yfyx1!sqZ<=g;L4-erEF zd65yZapCF7VQI+`nE_Gd!Ewcrk@0cysmZZvWoa=9F<(+*b7GUSlH-e_lgm<5i&Imw zGvjh|6N>YaOAAv=Gcxm1Gs`kFL8Xb3;;iz*!jk$_&$=}4+QKsQP{iJ3*z!p7@=(;? zbo9zpiF?7YcgavvLUmDkWll)1k9-(=s=%*fbe|LA->e5o5g3+Tbl zDb(=fLifx{-`x7p($2ub)+DH2fS5ZQVz}xSDr~TbMz|tQP^`eufIxY14G8LSP(bMkrUaYrVNxt}KdT^?Xe?As7?JDD?DO7gheSxj5a9;@%Hj^V4T6{inM7I^mbw)Je{a!Z{!~}&Y?LyE5V3E)F9Lk*f3( zmny4BczbpbZ&kbMr8p?l=Vv2ui=r#2VE%TuPdvV4S`#e~-x>R0-L+Ny={2lKCsZ!j z?u>7k!9t=OIwk&jq-ktI6&bNe)^NO` zX1Z+fiUS@)iIsR^br*MX(KddA3H@C<^_mPxe$n%>J1gG2?v%{EMvgtIDQPyE4@1v; zkMXUHQ7d)q>D$c7FA0R^a&|0JpLq8Yn|O^(*f4G5#>z@FjPuMZdi5IHnhfj|mFM-2 zd?ax9douf3XVY2L1j;=Oco*BYs)WsXC4wd#a2X?CzUrz%=)SP_ED?ywxh%1d&M|lY z8Zmh96T57W5{Y6B)k|F!zDoJ)X%;<~f)aBI{Vp9mdtGKguC_vbH5iUX#2scU)?{hV zpx@tbe=@J5`$+)H&_;N&4qWQ7s3VyoD_hZ=(tS}png{nMw#WI+EMZjI3Y2&>>Oxl6 zT4j&(B$FFzv|Y0nFITOj77Dh-Cg$SIuxvxDmPC9>;uCZE+;um@1T7UzcJ25bcTe2o zi3##ssN)36BAeL}pNn>r9w|C4FiE;fl524dpIKkOdaRDl_qqHz>$2{4PZ)p2?g-X7 zER`UhHeI|)`nzw=*jCQ>B(NyJJl<`~kl|>bz&mT%@j)rlp5~q|-q#*>YOSuS6AKD1 zw+N#t&Y7#T;hGZCF=JB-wM3EWa=F}G7p1tdG(mb`_PCn13hi={iJjeGyZR>G=?IN$I# zC3UgXWSbU}6m-fWch#F>+x2$7aMRQ*n;%BSU)LaU?G@3JSaO!_3KkhMJqkNm2P;3d zi(I^nv1?5ipa=-*p~KVN+B5vso4SYFRQ3yE2I>gUfF25P*(*DvZg;aw6)#7`=BmX_9SJ; zUD4H3H1QPt2KO!oHi@%c>N9#r6REY{i$G&0)Gl3xhHS57#0#ea^sO zo%lqxm8AUa0e;>ZKHA6^$|Ja}4^r_e7?mA)%QzAudg_|G<<0mqXPij{p&?JDV_Es~#9`)qBYG{dGBk440eK!w8PN@7%$fo(N?Pv)SI%FE2X zW4Ad(lDT#H9%hW)!d8yr$CG?HnVacxj!Q#!cqn*iGq0dDP3KkUutLWsRmHhv#qf(g zYpIhtiN;Hh68MiPb!-%Nc%+-(sAQ4vvHxvWG4;`d&?niRo`tmmd{6JPsV085FPV9; zcxrCVNr=h!__?nLc4o?p-IE(1wp-6rC-Q$p$Dc@>b4)gicsq8GypcO=hkR;C2_qDT zS5kPsbP#~M%sk$%qHD`^y3#U1_IA7aB>wGl*x27Kq*y>LTKoal7#W_@oK)o_mA{5B zd5u8oB9ZJ>LMa|{1(31(ER_-~^D{aYH3oKdS{|Lt=X8a+wJ!4M@d+4Sx@g3A<<%8o z^E)RW-CScYU zYeuS9jUQgqkQII<@tfI$o3EY-o2p&WSHEV=C-VEvTb4JZY=lK$-x9O9Bl`O-2`f2? zH@9VMq@~~87ybR&Eei#4OO-p;s*-QC#mu#&EMF+T*HV9>t^QJ9_jg0R*M|C*;5*7tg2A7bK|{N5$ujYFc%$0P@54_8k=PrqQ_fY5+XpFg;# zhT2Cvd1r)wPH^(c@(B9k7nUFIost%ul^T>45t$SkTO1f!921uomynr|l%0~A8<$d) zno*dVS@I>fEGoGoIjc6Wq_w!LuDZUpwxPALxxKEjv#GVWp>3e0qra_lu(NlhrFW`j z5ZTa&>>UEu_r;;HxzUM5P>*U7L@X^YPtVM5ZO-j%E$;3h5epkLi|b3MwZ&!hR9YHeqEWoLQy>nd={z_^mEuK^#7t*!Nqt+kB}fY^3+*3oM_D{DLG)$NtFJ@ne{ z>iW()h;7>1UImyAIA{PL3=qY%vxC8I`}_a>VCuj6iU6#|h!0N2MOO0McED9kd>Q5K zOzuo^_JTOO&&S&r3~Cayrs~{S&(RhJQ%={hF^eXvG-eYFPqeDWaW!HqYo zOUlg_=OXf7?!fG)KL(5zTdr8GQgh#r7x!fql+ohqWUIC(#N(#s^41XZB9^|%Sx6Dx z6m*I_{vDyutXMeP2^r211?`sDbFyW3hA!%hxiASl5r5mcG~~@WKNg;3qWb6!N~ftO zzn$r~_r5&O(^WdhW>axj_cWtgarXM@f`mIWy zQDJ{)Zuo73_^HRTOJphI8k5EOgK{R#>?2x7uhTOz#_6YkJ;h0uH>-9wCd__H?#kz`v9k#SzC`C>us1|L zTb&xR-Z`ux<393FTv+(->m)kT85dO6ozRHHa&j?9G>v|%6J}aRr!#WJ#os7z{xs}< z)rngqvRdl1;g&-?xUR?mQzurt)MK{ivOCzDLtRb}JrBzx?114(h+9?1`j1g0QNAl8 z80T`eSV*#=y9~3XyelP&M}4ItlAAv8*x}n)AYmu>>^EPzYB3j(A<}>4kJ8?XABnoPFjx^FRS^ZMf z;5`$YaMsin|9(DxQK~zfl-0#!_;1Hc858!O7h#F3eIUJfp8`d4e%Ka=jGEj)psfDd zfb^LJA*m@Y`n4b#ZV6iYv@+?G{juD0{k$7Jm#6Qfc0WGDq>Ft_82yTfC6OP8^!_Z< zVy&{SHBZ0lu*fZ(SLmj zo=55-$-F~ZJruq<<94gVl87x9ewEZ0C9+kJU6v5zktQWA+x?piajf3~;`PhueC|kF z#PxT~SLS@z6s`^hrKs#7uTsC*U#Z(O8Skx3IhlZ@pnic(#Crd^HBsFbdhczD-z4kV zd-Aer6T9hrEIT{5cM#~#6XjKV=nSpCK!H$E%Du#*mOl5ju(2U2@W1~e$k>tx`S6-JzkbA-D z>V6dSRs%K`-+B2jF5+kI`y3CWdqpeM*vR{-ra__$+sD;`Q=W;wQH(p=Czng>tWJUm z55mXm-Fju_B&XXQe=!<;Uc9j!)Vo#lElv37v<(}Hoc$)UCtbEZiF9vuN zzw^~QtnR)tNX6US*jgE;JH8>A_BzMHk0loka?2A4Rp6;q+&W?TC3HmCj#zw0QiXc) z@oyiz9b+4=#ou1}h!pQhBNMSTE{eqE#Dpui z(RNfW@TgOBvqE0Vw^B#Bqc;5$CB8~43MExhT@&l`)O*aYQzt3C9TMaFkRZ|3W zjnl`jvD#Z3tVi6vsC(PdS;2AaV#MVDF8K*+1z3;dZ~R4G*C+!-Jf8I=Fpla;s?)Sm zA;hFbi4?rEU1N`v$>^SP)De8n&`p~IkJ1^{J=QYhn6%uJbR*YR`DtW(PTxlA%Yp#a zt)~96-Hi-e(o{79**J%$-_u>>7H>`2x~T~D(H7bo*)lHylx znPk{1Xc#rpPVy(NWVoDF5}rc1YB0R;WUI)E##rz6;s7_EUPim4iGG6H=<27}nXRP; z-VDPk+ri`|1vD=-8sCoX4!zDm8fTatv$Z4L-YSQELoqJP!m-I879Tq$a`{9H9Pc!ydyAzF#;%-AH-r7y;MXX=M5YEu6~!i{ zMaO2QX5_@B*OaAK=M~lzmDCoOHO4YqcUbaVsH zwz10g`L6z{&cTJDv4vsy((nXw1imyfu{<_~nw(ykp5Fj2u_)Ba;_}w)()JQ+dkMXb zM1$uYBvatnUD5v%0>E-uMP^4aS=k(=ePA zCj9!a{fIRP4s6eIe{f)DV>v1TcNjG7$^Z0KP;l_epimf0<>8YjQ4i!rgg`00l%y-E z=|a~MuL|X4C8lR57A0LPDa|Vpz7Cka8sQsF%_4GYCYV~X9S@Y`p@+(Vvqj>3)cm zBJl|Wy0{&Rs;roLPo*vwrVxBsqaT-2@;;T5mTUmOW>eU_P8$vZAdy-ctNiZ5tk$I^S%+EA)~PQ%s;^{ z;hHAnquyD5K$0}2F(6Vw-C?Z#j zySMyDZd&Z`w7!1+{+r#2``Cvh5=rc%NINTDE;f5&p+si8{*yuz_B6{|a7Rj$L^Wp| zhOLSBY_3vKj%T<}_+#_eamBdv<`oRPT|^MZyrvtXWAMDz_Bh5XwbZ4;N9y#OSQNG} z)teY*9C5%R-DBsE^^3&2Jrj6O^`rI+D>_z0u$J%Ov~^2ipQfQ`z&s*Y)h6Uo=!+s{ z6$MK~((LDVn}9`n`;AT~Tzl-;T$Ib{g@i~4+J&gF+Y6#1Sa=ltNpXG;4dz3-YF$As&gO-f9xQ#N=M5zWn=a4^5|EG6AOc_H~}=aa?6Q>)H~nnlZ2iy87r1D>Rs zF`CpA0+Oh?FC7v*=c4PL3@sTnIyt8)B`~~2ej5A~Y?xnca~)YY7r&>QiyNG>T#Pnx zQ!!a8VnmhBy6$~4Ejs6xT)K_V%l~=f+I4g#4#Cmd@Q}zq4zf4k0k2rwBYD8aaqv?9 zzH+32;e*Hbt^di{{9kWAJU0c55O}v@!9IX-t0BSoiC{q4A0fej1mh=y0ci|KFd)JBsbIihyfQFW7{MQD?4n~5vGG7O zVboCs?cvHs7L;%QbJY|Toq!3#wfiB!_BM#7aRL1JW3f#(*>iq%nR?V?b*RNMk@6<0l#ef>MxR zK!WiT!GJUdBp8rjK!O3SF`zZZudOi-9S|`dhJTj%C@L-oDZN106gCbC>knRt*I_3? zr4anTLc8!W6?8{`*x@4V1dJH$VhfH14Vw_N1CpGB=g?X4@Hzr8O5g@b_}4BNM@Y9H zPH`Sa2F7tz?-z3l9ETS_Y~==(e*YY0X<21!Ysb*=C>Z7GqkRsqAP6$m{<%+WU1M8Y zXGdo@HV)H|1xqe~n!?yWgrR-+rv$#5f?y*K7y_o^Nn~Uk2=2rT1lK`R|K9h{aTZ2& zcokc)4WvNB#&%Hp!y^-%;nEMoEGjCiZ)gTd)39-G zE3fM4=X_-4+G={zY^wqw8dNlTm{?VKMdjL8!^8C+X1n(vAV9doVu>QrXiWG zy0NkP4Sv2$f|v)4_W~IGpEW}c&x`wKG3SGSI`lokH5@E(urp>rFdZzI5SDf>_NBKt z)PgwIJ#j3%aA~jOp16TWD~x|an2=VOm_w8Jlr|Z=1_hh?aqT*4#>>>q&uN&R(Q-Vc zRWG7rd`i#ol-}hL1H%(WHWx-M1QVSy6BE-(&{(yZ=^nGNu(00rWIJ^Z~}NukJj{N8C2($mM4&QL!%!>P(erNpI>z)h~eO+n2=c9)m@E-!^5uX4~iho1AK z@&crC0%Rf=h@=Gxr3Hy(FN;}Uwy(VGJtRaRC3H+$=$Nb!4`hX2NO6!%EzC|WPD;xz zN-K`f$S#3q?_Zm}F?jzUBn}cAd1ggL7S<=iubwKIK7FeCRMX(8UeQw=H-_Z@Qf`WGA>w^ImGUD2xz_E zLZ1kPC+0+Guttm^BTbB=qod;*`Vz8R64hWS?;)|--pSS5+uuCO#qg~~;@j8ZUlW^O zP*i+CQQ`ie0RICN`3I5X0LNkgf-TTfKnWklb3FY^NB_gYOaNnHE{!?3u(%W(gR@ew zzN_zFIQ-|qBLAqHPXb(l^9L@9h>8Qqg5}|_j+z#X`e%6I7rtq_Lpbw2E5MI%>>obt ziNg^ep`E}_k?~3C;7h~Cxo@lR>*K~?slVeK48!;j7l$TRXkyJfILME6FR&CYD6T7* z-7j=DDXJ_gE^sO?Z7A+SlvXsASGAT`H&z@RRMv)7`8}RjIK>Z}G?YrYc1$oC)UNgEiN9dxo7 z92^|>xi&I^9EDGh#T$-Kqv7z{$=S83nYpRCbxeWGA{WHLx7qp4xuuOcDZVHWr(zey zFVys3s)KIuQq%C2?u*xYFD(tttW1Dndt>|B8pB)vq1%XFl2Fzu-|@)7a`{8G{i7nq ztQr4Y@s*TT{=nTsS`pHUkXD4WB4oIM80i0MqU0BNQEH&5@L@EEL@*|65Ydr6JtmBe z=2zKGDi|HmbyR;@aWVGx=0B~xkh+7^9i;C5O?3y^1)xPBvV( z_g%=#3$6y^+^1PF0q2Y9%@8fJnGjxd8=fH{Cy_{7BY^vo<`Zf+LzdE_#3 z83jVHQ7AMDy@Fl=ZFOaJ4bygZcDJ|pc6N7jw#^OC z`qb2wt2*<#KYV@^QC!$K56isBx(rhN6|G{Tz`I?$^!0R zsj2irjh#P@jNRNU^T91=D{(+UOtwAI2*D0@=3Sh!Zc@6l%=9>J+~K^ zQc@^?Q)cz}_d%M;f*c*qv$$U@w4Od==oi0cNNF_ifO-(OZ9k3w zaw{Oe{^n3cG-n?Kb@F!(p> zP2KRe)GM{VXB5t}(rq86a;b#HAdL~9VmYx;%o)}(vs~iKCYU``5STtQ5TAGH$QoER?*;V@yRPUyn0tyPvoSiDEP@=dSx^2t}&8J z$w{-Smm%oQ*UGzZ<;0RWy$lu_A}v=dccMeDIyG{>eVjyxfZ-6bibOe3MRH+%WT;^O z{LZF-_BY*E>t{&WfKSm5F5WLVH(`Oh-vV~=P>8y^x zg{8xWL@t2`5+pUi2-GTQYisKp8=IS(e`GZPDGiR=?(Xj1QNoz7-;Nqc@pgc>3*W*z z6ryya#WMb37gOVLOTKO^)Is?mp%gE}gQ}QU$L?H+=osLYNn>~JGwMx3UML)UY0Axa zyb+N@mTFQxu)Iygmk~N=asB4`@wHK#_Xc+MIu4E+&MvC1ZcjWsAA0-T_w!c>40}?DVtSJG*<6z@Z6?Fw9RE zITU1sA3%MFj)<-Lf3^1=P)%-K+DYggsTLFv1VxG+#D<89iXw^?D`M}69X0f#(xfP$ z(v*&Xg{lOQ-lg}17DyqLK&W#P)a%Upzq#Q3oi%H|^RBaA(6xx=?7g37@3YUdmkNr{ zd~Td%na9UicA=#(DZIc_X_NaAIn-1)gA<8vgUxUPUoqL3{DSl6W0ZlDEj*-6as}zA`yu+GJ#4aQi&uQiA3w?yy+w|olK@vz`LK(-_M|c zH-$-|Fll5Ot)EV#FlZDe9sCZJ$=Op*h@>-U12h_oNgv>B?Z6RA|JU35wRb-A3)_V~l^%;Ij9kfjc_CEj_m;%;H@iuwq%jP~8DY;4R6ZSDXA^KMM`>jCk`nGRiq7 z)=o_-Av`Jht@p?152^0)88_8bzGPi1=FsTi$T-(KhetTiC~(k`(eaV7@$bq`nU_~$E(iV@)MH#xb zyV~DS?$|u2pd4VJ?Tju<&zhyY$o1jl)Ak;BF3+0htnB_IV83VJz2`3<28HZ;6@EYT z?dFK+Ymsq?n0F`RBNIQEC%^spAtf{|F(c&jyR4w>nB2g;=mO88h!T&o@Cy5?SJig4 zFYD2b&ztYI2DE$O9(UgE?!gE5*%C?Qo0PM(eunA5o53))&B*B3{;@Su*UAm-`R2@3 z@V!=QXfBqw)@r7A$ya6BQ%7z5mR9F`%+s+SWn7PV>7#Nq)PcV|LeGBaNOAC@tKnrq zdN&@_REdEwHsC^l@1S^$9{x~k zgjfi%5Mtppu@LGk!8SG6ruG|cYJYoGkE5P1k66g9;M|ID`1I*hN-BUw@T7B|jEwa3 z%+QRA(z3F$a`04epUR5L%F=K3U#pr+s~gK}8Y?R+tE#F%HSSw&T}@35SE{tGuAvcB z4S+Byp|6iXAP~VKf<*+iAApKKv33%1|M{EWYBKXjz6qy4KceqchWAl@V4dK#qvu}h zm9!J$=w~l@r5x=Lp-QQ(?ukR`ZpNCzE(^l%U!J4KYUF30Jh^( zN=iyK;YU(td9&wN+?yh7VNo%7 zO2Jdc**Nabl^1+#tf*-O`&2!-L-qAPcc`hc5tMd%D=S9nwWIX9%Epe8PDWi1wW@ar z*MY-!bb+D{sODe^fXjeW2_~qa(gueICkv>Cj){$n zk2NzhUZA#e`EtFjiRl?;TQ(+|tT)=cDbFM`(_~xv?%f6H6@{26(S*AChKTu%Z`QW7 zM+ymI*I|3Y=gyN9Ygvu&QBs^C-l9Um@}eZ#^#|K|k(dESz*7vuj#v7Vdl)qkxkp;{ z#E6-&@uq#k;zvN$wKKy2)=5fNBN3}1$U%_%4UmH=6-=pMN;SQdigyYm<{IFJD8LOl zbrL}FCJ)`fIqFYH<`EH;+&I3Zcw6k}RY-_o5X0cirr#Sr1M3@*`a$Y9UFtU#4C6LJc zZEx@Fz;$$Vbai%ifo{I;uAZLmo}M1wU6N?)h|hXI55hqsUcwoq`6bN?8_+}aN*MGI zVM|39c^{*tMnp$^TJmK=H_Gtgyp|a&;F2x$<58dzY8@*&msK z5Vf4^;BGJX*u&Gyd-KfGYN|fZUj&-SUk-2!3OytpW-0R~%3I2a4*eNzKm5{hD`t@yGn)5{o&`2nlf>{@I7-Y9cU3s%i?Q-a5P6%{n@J@K-0# z9blFKV;PKP(~f130zkKcZZmDSfoKEW2D;63-DWD?p^C^N6cRBD;ld@TXOIXzw$r813NC`AlghDZ6Ha5Bn`UFwA}`hH0U2;!T)mV z`L~x*WI?aME`%1h+qR|^TZ`?k$AN`!>>@UHlbU)+%{`>nzW$b8N=qNLl|XCbEP5Le zEJiz#(MDvplbCI!fp*dWwwH+OC3O(UK+mC*NW+pCSn>dlJb)#$I{F83{X;m)PzQzG zK^g9$u{x;i4(f0xShV2|>PRP8w2>|vc#n30MF(rFn*r8Tr5^abf9XyKR9& z=P%v1skW0E)DJ5tBb;tqj!uV*>(N;=Rp%?Jd+GQ*^Y!za|J>VazPg{Lx|YvtO)c+d zUQwnqO%MhW#)xyayuPv!m)g=t zYwe@8{fK<+1n^6GD*-G9h=L&gwG)|bL=gNy6db_edqDi7_V-fz`)Cvbl}e`5`UjYl z5mqmbNW~Hv9b`I|#KeL~$&HI0;CnI)M`mHkAUY0$7}?Pe0wgC!c2Wm>XoDa|cK!^L z-vi{2&`2Na;zUPIfTYu@K=hai#Ht6tVhpnAgDeJ%#T*1{5Uhd0LDtaVzzCbc9vT=L zVzD`Ekj)+(25We5Y-E7V9vTK~cxVJ@?hLa>I8yHqOusdHTTOFvbIre+YDk(Cl$MoOlsRZ?A#}yW1jNMo8pQ>qWcYbdNZ!o_CMpD# zHRLt&Yw~l~5XhRE#5G?xs|=|)s9ZrTS$1-hv&iffE6`pi4L97ASG{!bR-Ee^wV5wS zA$m`QzRWjqr=Gf<(7DC?^PU*nnyovZYA#E*Df$-q7Kj{Q_ff8PDxI_DYMa$v2gcKi z*Bd>*rQNN;hXh(CW6;Vx{8hmRud_%}Jz_6kZn}9Uy5olWQ;l^m`diYHZf@zqwe)tkkqDi=ypiH1ORZ*etF!uz*1fu{rAX->qSUYYkk3_-P72GJ9fdXnc%4I?6GN zi81j0VHQ6vgDauUF%UpZ{axE>{?p&>2qJ~DF*4O0(LZt~}jkKOs6na;Xldu};ItFc* zn>A_ZMb8%Q{cdB^h>V;oR7Ewd)vcZN*zOuE zzM+%Q)I$c5uBnF;+5nfE3}>8J#wB&_L{3m^B`{lw1C!Ja#I<%1o_a~x-hLbbL@{m% z$wtUYPJOw(gN&eqi;J@Q zL-nUm=eoMj^|gbXh)AIi&B2C3}tpe_j23q ziHYN~F_5Bml9?TVll8MeK@s!6@aIPfJ1Xq&L;A?et9ZK2JGrEK<5gz=Z>GAuv%n=Fa4rBj4C*Bf@ zKlO_g&`p+4igHg0sbxS8Bgf5Q${TPXQEBYJ*LU>QUQd$1%zcR#TU*GtCrfhj!%It{Qy3bv1mn?!AH;_)-(n1pXEc?b(C zGo0cKsLZf&91x!@%Y3iKbb?HSlWYLg9tXsd!za0v67Wf2##|d792#VBSS6QFvVS3y zw82r@&=?&oHdx~f&YEBhPfXHFuAKu52jXBdSK3}8lqxs4!Ee7?I0+vailOYG}lE^WqYvkU#O>&djsC#(}T%3Ouj$k(LDqOEa?r*6i2KpQZdUsoE|}X}9&|Z**!6*h)17_jeWyGRpY+&& z+S}r^_tA^~rz|~AUUs>E)9IGAoz-ct3zz-QUVDD=X7JTJuWsK8x_0;V_4}a@U4kFD zMBjfLch@`F!TE{3`)jnF_fwx2KK{Wjp79QkL!7F@0#^z@O4WHN zBvWyC8ifiDc-dse2$?nn6np8QFOD-;Zh*mLj*bk_7(;X>n?As146y0UAqI<0W3mTW zY$l7%8e}tAqjc6Nl{Los8NQez8U-S-HEJasC}`Q8V|iMjp54^ z@@+osEqwVxjj@-K;q3`T6VJJKZCQ`{+w#^lXe;MDohhrId1q;8p7VKOlw-;=aX$*X2_=c_9eEi~kqnq9aFR?>;s9n z^1Yt#ayCSH{w(8=dMA1PyGsl22AEhHs~}A5XWt93wH87Iw73>M)eXakEE3mF4Ov7N z5>nMbidD%p)YPQnuJSqo!PO-#r@V zC8*o1-4^&5TOT`)2!7EU6%lqIR2yl8K+iBWs=|vU=1kl>ZyR82S?KOOvqJ!3OS0Rl z^g!&Jypk=}jF9ARD1?YXnkPC)?b5^LepqL6F&eWg|byZuF zu>0D{tj}q22-I4Ave@dHihV<5JkdV*0_PY`Xtr+GhU+Xcrf7lcA7t!ITb-%6%+)d~C1-Rd^zh%C$8 zWIBhpc#+O$>xz>p@z%bgf?9@E^K6w2h4hD%9NVhJO8?G(Kpal(im)eUz zAxeJj^j!S()u&l|rEaBP;kh(~^IR^@#&0y9*WKh0uxlt;cwr@4(9;27FPP{s&oL)? zNv6(I$6c-~y?W)+LOeI{1mELP8I^Pt(;xM+mCWCiKyQDWB1T1t=uxvJHek?Wtd@!d z)+b_7PSVZ#R?Bhr+u(QWwFxEGBHMZbA|8%X3pPxQCPxkG6xLXHeim9hghucce#U3d zw=J3_wWzdUJ!4ykbjEYVk=X~Oh%Gt#b?31o37gi7r@yn4vD*BICI5y`6@yf6m?ycv z&`@@^>8ZZ>so zl!2q|8D3?T0qqUXGdd)1E=401W(Y3TvlHUi!^mO~f~HT7^%&IU2}CIIUwaa+fOE~K zB<*~v&GQ^7im=noNAqb3MkrZG7N{qvO9y&2ytTFx;`hF|UUsClE;%)VFWb=AM^h@A zc@DulWcFmSvE9PGSbM`5F*$PfsRq5iqSfhA9vU~~5fM?_J&|#Um(0$^-7|MAjNdAq zDJi9<&Cb}-u~p8vL(XT?!(l2CBoyod#2+b35!mB~SO6TINwO+Czfs5|g=@o1% zzkcPt#hzeC{d&hD214i(mHkyc^OMqD4dZRTR?-+jg9*e%_iCO+yX_42?JmDz{r>a_ z>8##yDpFq5H>7LDLM;jRILG1l!80FZufFarr1){Up=`{lI+mWBv+7oy*MsV3hnx1NtT*@F@8df|(`-*dFlTn+q`K>Ai2YpuF+?j&Os}0$zSWB=^ zPJ9q6+!feRJ$o>8n!TEx2M8xucbEcrg7?S*Z4|jlz4GH)9RvJ6%<8 zt`N)gkHPzC->NTZW7*5PyiHVpWVFNNgHJ&G+A@l{%bEjVhHF;CXX2@Xn8Od{5InPt z6Yb@Lv0}z3jHJgkN0HJBVT++0-a>?`YAH@ULs#j0!@}Q^I39q&*AdIOC4)Kg2Y=;p zdR!i-FJ=NroQmweMKkuO%{(w)@fhH7%cb@xDIJ@oVkx)y%pA>gstYVtG|p%)KC@8g zyymh?OP8Eqrgv3${f%|&t#>FK&^5fXVf(|a2Ij^a_wC$r$bA1rgY6FwSUoy(+2-8U zTj#IczF>X#+Ui@*r~z_`HR*w-O(A)yfwZ<0QINd1tSnVOdIHsNb*a>1wc zuW6t2Grkml$`{=r`I5RozrC=!V}M5YdrsBAJ= zGi8~~yaM04xRr1$!Q{Ih9^Iau%zmggYlft(mtiC)!Zlaew zQ(Uv>ZC9xE>5>4g8>!|+)?0T5>0V8!NxRYGp}5{&m~S>yz$@I?$#~0)0E^Moah=_}o&)V5*N?n*f=mR?}`DwrH-yg)kK6s$HMc2Z;G%d5I^( z`JYg*Ss4>im*?aONiV!&lJ-;}V`Px}XwJ7?;m%K}xPtaAw6H8U)s)ppj1*1w+=*Mx zUes)RQ6DT;K=qC!@`^^(5}p|+6{2v?xn=4Y7bWMJ^RJz^at)GIczXwHJL5!1Hov^9 zUf9*mj!37=>qLf6@+WrNJyAmGwqJHGJkaHvSEh?WppHa$ z`}6F^TKSqgyS?-~;@{XEu-iRa?K)#4COc@>^VJ4C=Mv7Jb#WFwk^M@huL{m&`C`uzXgrOBeOvt3W_t;|i7hh7!k-JDrDpbojnecg4Eb(0ts(I^& zn*EPS$&EE3}JF{_VfFk?m#DYO{9cH}6F z_%17H%OO%&+PcP*eyM)?-CrhFmFj$9TNftgP8cdAIZN+td6y=?< zVu}_<1}`g6eRs~acuDwL>k>-Y0Zf@5KA2Iu!t=aqxj|>Cbp^FiVMXOy>7y^Jwo*iO zvbW2R-KgFr9doT_^WLZL3r$bWRjf9@jduEI6>yo=c=qCh8v;i{$gJjT0r9txmyVNH zhMbQ*db`DTP@#3O<^G7)5Y~#PZ57T{{Yo6pUAIlI(_`!UI|koNX@3hR@(2M0KN!Y_ zo^&h}ucPPPDfjTe^7XEHW@d^?hu6aLH7s9GmanJK=Pv_1?qB)b-#V=9fAhoeya@^w zL!shn3KdVuVTBQjV5kHKBtb(yYQCeQm)wKbb@zU~`_SX|^X^0ap-IL4DO`S7h?ob^ zm+vS3@;G`PnksfngP%Kv5dQhIbN^pgGJgEE>q^vgPg;2aFz4FS1tqBK>q50g4Y%y+Ca3Kw%b6G2Hgg_4Rjl*$_!PR|F|kM z)Rus`4a{w(pW8r^21yzuX^^Brk_Jf{Bx(CqlrE{*WT{G;sY>rr6BSdF-m5ONPhIuc zLaDtAcSmYS?ADN!SuAd?(dN&i4a{vI+Ca3KHrhav2Hgg_&9vPHq78H#=r+)8U~U6*n?Il1K#~U0 z2BHl_8;CX#ZT>vkz}yC+4Mdx1qYWf!&~2dGOxtZB+CaB~ZUfy0<~A_5`SZEW6hz_X zAyg0&-<4CtBcf3PKz;3E_F*942quqDLHUgzu>dg}A;?v4ON{-568lTNO+GF zECecWyxc*|K-lbE%txS6hvIXb5vhI{D!##SN^qRg9~-BHL@eb>H*#j61J_H420Hei zf$PoHRog#R_eCJIz-LU#FN4nj6VyEpK(~c%3*Gj&+;%DxYk3gM!Ng4@0&OH`#=%OI z(GRg!D2)oGQK2*{6sxPz5!FO|p>29X zJMRo_91%c|ffC@^ZLir#l9|YxtprFGWSkC;PC}+al2Ie*}x0z;c1J9h{nKL|d zhG)+3%o&ojKTgtkr*P){WB8x_wR`rr?VsFp^0@Ep= z_uc~pl8^==p_jJ;>hHbx-2dPi@BHJ9@%Cb}6NQ}}R@U6#oO8|jO*IWwg@ZS(AbDsC zg7|nZZ|1$s&!@9tv)(4bt2vF^+PdX+=WYWrE#63d* zYbL+{{()nUcPLuQD_R~oZYh8KnTpEO6Q^GqnmsXl@#ilz6gKr;BM*g`I%#+v*%lf z*AXt>Up(H$`vql$e*PAjQ1T_SI;)_rpt!80ysEmsv9_VPuDQ3Vt*^7Yx2b)gwF}kJ zi|*>f*7nc$4&Ymc=DX4Jeb|M8Vd5x`fF}?ML=tusha4V5qQ+2|i4h!i1UE$_jS0ffZh|^T zq0UmkadK{MZWesc&(ALaSpc%IxG=v6j*AORi{QApw6uI!q74Wh|KtpaEUh_n;==jk zN?IrO?AXOX2d*VD!^p@29%X6QrKt(Tx&%ov3MeOs6*{WpC3P#3!;c>}-y)qSmU6Si zgP*Ucvmzy;+*|nI*M}l^*aM``Yn^#>E>c|6f9I_;fd}QQPD$;5sCxI{`8(B;+B!|B zE6pc)rOsJLU8sD7-Y9eVljds=XV2Dnom7)v6W7mgW%V!Zcc&MRtqn1ZhV%|^V84=S z60R61dccD({oaJ;!zRNWo%xExb&((VByWD!4my>sTD zQ_)+Gd0_++T}7~sPt~JU&2sjdw6dT4az}1c2BBlkO`{%y&uM24L5DS4kL5Xdt5m#| zQCmc>e}t|MJ>b7?Va5Ve8@;{i`isL;xX#!IOPL>abDwg^8aMceh`G#7jj0KpO>?gF zojW_N(qCUt0G27Uq`n1zp1u>;F%A)@weov0`i>IwcEQ}z7Xs)xO%1J>1+RrYWZuN! zB01=`?Rta5T8{k{eIByD!Z$@V5zz7V()w0qyn4osrMy>7l?J^PnFE8Fglxv>Sk)zB zxB2qr>GX4JZhI5zbK#Q4r$-DN$@J{J>iYIfS6rkMv(!YN!B%E%}k^O4@i9Ubn*9*6nl!RvvEaAnRZWZZD-5M6{ly*b0@ z;3xrOmd$qNMKPQ7*`f6zQCG#I61dId;*#l?I^$wv%1rLLtw#wWlCqB}A{gi>IrkDX zdFO6?sgU>}n^LxaQ^?l_(dvZM771=bdh?}je0t+ZSYk$xGLDeh;u4R~LhWBun1j#2 z?O|I3kB*F&Z*`Tmh+A_Z4^hSUC3p{mL}*QH_1^YV^7Ol_=VNM*;KeD?QJnzh8F_cdR*{2JY=YLu8BqRU61wi~;grgRuy zyPMIh5gjtpr5r)X@G;-oJK1AWEO(&qk2bz<2l~ZkUmQRxv7b@!bJ==5tItv9n1Lcq zSD2V-y0QUcf|wx&$PWAi40=W=d* zuXMN58TpPf-Ogg>In2y=g!uu2ZLd3r*sZmj_N;rbuwly$o-MjOTlDxhUE|+oD&Tf` zlT*zmZ>*5O)h!~zTlp?;-MUo-3~@EIrEir_LTZrgP600y;^G4=Xz$( zTZLRq54yVP($!n0x;H#;md)I{s$+QB$<99e4<)1?p0=_0f_&tgXt|Ha%JR{( zXV0u13~e%TFC7LP%Vs|Rcc2U~(2E&MOWCfbIRz!Txw(0{x%rt31!a}R*1O7HIF}F2)K^s2x3o8e z-ftLRXzoR{dMdQOJ=NOU*1ELR_RXa|{$nQ?_}Ok>#h$LA9^z8(qicPG!$`L)gGl7i z!oqOh=W@b_8bNM<8ojwZ#2`ebRu$Zy1*tVEqu(*g_stQ?Z3}0%#y43T0 zX~=&Go3n!Dtc=hS6XSQq0D8j*u|jMR`?B6-=NHkla(Km76V{8#epfA6cVOLtb?47@ zM+D*mn8UKHJCQL7^vq7)vDE@F>cFT2qt5?|I?S*+2AgBBIreAf7;J07wiaw_{fVsw zD+a6>uwwiV#aIq}ZTvTZFW}$*_^~|*eD(Dsk;nmBbd1SKE?N+FRA5I1c2xe2qXN{2 z7dZI;TUdEnyO<$f5CyPX4l6_ST(k%PVSPk!TGy-8zPvfT<=q@6I0Oxcpy3eopM;>7 z5ljpy&^n0zrxJm!o7ijrics%>xWNxsc6Opbl#Rh@GmKm?a>2;;Z;%Uim|%wqc9{N$ z!vx!5upI{5VSi$W!HNMZ2CNu=q8RXQC9D{*V*H6>z=#1W2CNu=su&5&lX8X4*y`Nk*7 z-8d00Gl{!tK z%upw%sgpC4lhcz^(^FH^li>N(%+wS(&eHy8dS+^RZhB^JW@dh77RbWv?EEY^&MnN% zFU|v500I}0t=z=_qeoCQb|sBpGWmoO?=CXORa()QtKAmO#~o-6yI1`3COx2NOwIdt zlv^;EXp{b-Nr2eT(?{}1fV+&!f_W= zIiLfks4On3Xsfr*?la>`pK(0?J09(?)VO5Wo_gaOH=FV{!!sfJddvfd#G}2lPu`w3 z)J%)v5VZEIv)av2{(AF4&g~zA#AGgQ^btFGX~5xhXmx=6y}p`k`H>@rRZf%VqSCNU zMs>kE2$AWy_IM5dH=93XU|D%BaBZ)HMQ}= zA`)(E{a#Dr5rq$gp0|&dZiI_E&rFOQ*C|kNV07maO}?>orXb&vFj)}Y7Fuv@acP!L zXDho@m_E{;G0v5XO(H<6h?Tt1w$*O>S^EuJp024I&XZS?3tfdRlId8oPZ$rLm|`*( zc{C?i9{RLAG3(}|c_sa!*9ywXeePsubcEF(%&~ebn{|AKULPhaguWCqu(~NYsT8>N zouzKfjV*Lq=G%NVtD*-y`XfceU)*w;8Gc78G!D15WQ3HU`y>2{zIunsTmm)>29A8R zB%{$mxAq6R1X-#J+cUUH1Q|0u+40Qi?H%7McS3J#UJ(yGLw@GUyhS%G=9A}scHMV{ zr$mJZp+Aj~9ho7@_)#(kupb-xj~(FARh5ZYvd4x^qHG!B!# zYHSi3oj<)frBv`$YtWk}OEo_&<)DOEYu~Ox+ZJgPLb^?#KvK?KO*ZkbZ!BVP&PFKh z`=8LZPI6I|vx2d)a$g^^q-L8u-}!0Adhg9N-%p*S*c#JksU)Yq!=X)>nnvu3-eTXq@%z5Wth&j*4)hwm( zBsFEg647>SGx6jf7lQamOF5+(Cy(xWWLi-SaUe=0_P>FI$CT&p>+F**;xIEl-k*D6 zW0X|U>jz>bEo?@QUBwTLdkQxcO--lD70!4(yICV#(C552NN)Yzx9smNYOKm6qIRFp z6>JY>DPC1#hP4VjN4PJYyC=V4 zSn^TqfwPyYoH!VUGLLj_pCDA8yPqa8eIKiG$5!JFzcn(9H!H#Cnf5E>rwehmPh>I@ z>yOcMA|RANy*0bA2YNmf(d5pZ$VArgi|NsuVrDyh!8gH+j_zLuH?7e238<}g0gp{h zO3>o@;CX% z19`cudu)sfz?nQo0r(8+yJ^MU0Fh~+Ov7agm4?Ouey69WK@HgS^eh<51>K;Wd)Zw8 zwcMb1`$zrue-v^4*2P$s?EX-9baHlab#wQ4rM`Ekx6d2jx9|Mk`@iuF`Vj2qH?+a^&Mx#auZ}>5-I2@6>a~*`87@8!mE1H|8${(QDSF~WL z-q>QH}v9aU)y+k=@=b87Wx*Eo%PrseS8h3rR8NdEoKxeE!%30(Mf8L(m zGT%%#7wFjV#~W6CDf{MYe-Z18T6JnMgq=hdT~Z5cCz zb%|UI%<2UqS6CMfrX)K+arKS5yzB{d!`p&ngk9kp!MdY=?Ac-BKSfG($&=hAtZ3hv zoH3ozJHU~vhNCM$TIHbOKm!4MoFX{V;j0(5o8h4DsJK64;=h~gFpXU z@P`I1mfxWzG%PH;usXlAHm{_vva+J8x(1Be+B!se1EQj#p|PpGy}h-)qp!ceqj#{U zf2bFU>Kz#AAHsBDDMPe@8Xg`215FzkGzNph5{5?!Bftk7rJ>R&fduG6mIsNn3_@ta zIEFBe1xF$oM{@(mNu;p>;>;Kce2ou}&(gqW8Ggpb0rY?`8t%{tMZlm=PEe;-P>04S zLY-Rn2WbE_0o=XG>8YP?;M5FxdI8iTPt7d?D57D}3=N6^7}4NpZf+VlfOGSJWMq!U zGD5?qALz79H?jgzfN12OBp3nEat&IB78hQKf!)o^i=F{uThAqOEg(qQ@RLH~%`AT|YsCOD6dMd69W@yS4_mx7*}f;8s3sHsysg$wM*Mg%A> zyKZJXHwng_XVnfUc|0Snn#b?;sZ3Ys@UeN;J)ieO7uA{NpK*s(`N}@0VbaaNZ<4=I>05dql{UDx%d(hB^Yno37AT}0n0R<_lY$&N}EURuRt!)9(k`?QM z(VVkFBmNs=TaBFWDHd6mEH~r0BvV|zxorJovwo62$%eFq2s2lg74)w7Y*A@?D6}Ew zv1a1cFC7U_s|nTx$YiTiJ3iE7(yVLm=k||f*_`>(O`6WLttZVb+1WcdI_c9rVSwnJ zJzt+>V)XJ=S<3|@9!w$ulSusUBogpu1iTpmZ$|w0%?L)?J_I25(lEPebo=y8qr126 ztuwl5W_I)Ft{r>#(Y|QKC7=`wl7Squva+(Xvk?eHYilbOiv@VKwuP^sk+moyK!}o!Fe-1!9V+XDbCXSwcrEKOF z`xo#|mi^1Wd`C0`;v}>Kat4p$K3&QQ%{QYvl9Q9ae*K!BnwplDmQEWP8R;2*8Fj$o z2BW6AqoSp|zO}Of7~YMIAnMcHynJYGZf$7+hIo5NXGcc|An)nw>cU_!SS%1MAmT}F0JZr->20z{#S(rTxh5zqvkg!mzvy|&P48)ng`7}{r%I?(^Yu|3eQ zE+B59+j*heWueD)ap2>}w5nJ;R3f4o{4%4`vc0B&1It%m(#!Df#~8;XnPOnC85Ns8 zC`lyEU!oi>rc;-#6D<26TxsgdTNfY-Cg6#C{L|`<;`F`RD`Z8hSXHW^QtL;d2)Hsm zz0-}={&}at?@L3H5N*F6ESti@La<(1T3UX7<_vnQIX#O$?ws+7hCQ`PQhNL^3_5-8 zctj0Xxe%*M$tT)dUmu?(QjRh2is9#et@Qb9_N~F-eKDtIKYdM*=G-0KOU~)rsvs-$ z_SJ}dTiX4|N;7St$#wOIr>^wQX{>tHpIQC`S88Esio@Wv_0sa+xQgjH3Fyy&W&5+t z<~OE?9-Mj}Ob`hFKKa1UpZqka~*1HUCv7Vpja?o^YZXU2(u~;YdfY+glGS%_PAf*_r#DAl*SF%$ouZo+(8;ZK&a^qR zm231@Z*JxGsKem2GV!Z`IwkNI(|uQy#4oAz^eQWpwF(WRP1e!xt27`m5cstrp`i(J zF$*l{szrw8`dTIZbbU8^fXUKe=g?$~daQZz=tI87h(j06Z}fekxxQC8 zL>R^CRO)gW=bbnYzI!`E3?t3yF`vKk(&^C^erFJaKe*P@+x*9TE&Y{g05dDX%!)9x zBGA$Q#;o`k7WFZ3llgI*0VmVbGp_nJqx8$Ez!?8|hg!wW?Z)yo>6d{#udp0ZSORW0 z^b8z(e^k`|rQOX9UO^F(`)Nfe{4HFPl3p=+R&i53Pn#X^MYBDMN8%!Gu)5iWLZR{#^_Si2PM^oP)H@8*Xp?+1?d_>7F%KDeLre9Aj zEH3|fDy@d+FYd;$5d#}Bun_}P^~g*e8HDwDy-A9U2wxmG8M4FjT=%1w++uL)BmE z92eLez5;f%X&cO-^a&I@f$}B#Q-5n;o0is~T-eZ8W+ve&(e%`Y0! z^;PgzT)|thC?(p|5^Wnuk88$k8pc3=aCCk{LUHTY>VBs>qFc?VeG}v%x>_`6PWK<9W;^*~D9aC#HApl>oBHJLg*nTwuG!%XF2r)ul!K$1~iQ)_c` zb4N#eOM55aBWdgG>F(+2M}qzk{cXJi-Ti}z{;59XKpzTIHZWN|L@6Jft{9pb86HM~ zJUWm_x13Ff8bQ-i>XyGjbsEUA1NCYcG#Z7*3xnE!CwSlo**+wgFqgA-A)VP850sa9#BY6BnGXS_t=vZm-^g>704bmzdj;u`d z8)a3}%;_9UHv;aG5~Q-Hue0!R4~|&IQ%(@uI$@na#0gLTUAKw?W*kc0-zYrty6{Qj zxIK@ss-0G$@(Fit>8mm7;YG%4_Gp#!vxcX}U35HhK&7M6QHC$+@F|K`Q?ZlI22Ix3 z+FMXbYv!eAclZ%S30F3XWX1GWTkgD;Y0f~pkgDXcafjwpyyREMlOOYaI&C>m34GS2 zlTdd9O{AS%Yd#$JvFf>OG2G1RSAkV5tl|-O<+`TrYsGT5$+r7b-|4YEued}UTp^-} zmC^dUGhZXCJ{+iDx8Y9n$2uyDS$9_Z88c#Dw8Iurhqp_tA_nqCTw7EPx2FZq-Tx>g z+>JyzYHP2i&&YsLX-jrjEadRvm?M(jtg>Lv!=^$U4pWWp+; z66h*;xWgn+Q2y$Hkk<#KMFzhVvd43N*k&Uf%r^4mj`JJDp*ho!oR1IPF(^NSb~HGu zQ<#v!7CX5$9sgA(oOQdKgy9{Dm^X4cEQM2cS3Eio#Ohi5J>8kl#CEmqg`RkgvYvz! zS0{pT{kC>U%v|-sv(`@?40qA0J=a{go!IUh=(!)XK;V_|`Div~IUw;6X zqNuqiDqy3D!rd=#&$QX3mMD5;3cRs>dL+$rLr(+4Ey;Val-k#~lkJQK%s6mux|Rzp zd`CIVT3N;1%rZKDZWa9{MsY&hp>x03g=>DIQ~pX0HSChXE*b2S{SB84_Nrj73ihgg z)2o8Fn_#qo(dM_&1~$@QwSm>K$?2JJ+7s>Cm|= z61|6ALw?Pvy{wx1*-jo{R#IS5S76gnU^}U>Kj0v&Hn7_Kw%Wi(8mu<3+WfZKz(yLZ zHn7_K4Ym2py&sT107~q5mYJ>+OX_1vI} z#>B+TB)D0~!-L=0>jy*lJ39Y2D-2evs7cm_Sim%HT%N|Xy1vZDq0FY?+}4rop2_;g z=KlV!!NGpiQ18f4FKVc71l3zSFkLb@4d!8J2swm8jtnEw!{7)K9ENeDBcmX*VHAVM zVTlAR0Y6G0;D~s@1dk&UabqL`NH$m@(fxZ9_3t;;nKclc`j`}}BVn2)jtB4T67_wo zDY#DFvvQmKK^C33^&#mi@t{jM|8j7h##nqr#^h|+{IBf$XokOEt!}uOFz8E=GH!=yPzHD%2 z6pJF@v16ps0m9TEacT$%3CJ{xG)-P+zz2yL|9-xH{VB65f)DvvIZz)E6;!g1)>b{)k!vJ%{#9_~*6Z@bgOY6Gi{{inpQQAXoY z-S_4|ACBm%*{+HGQf1x(#AwdAsjLR?`Ro?fdvd^z-Jic}~p$?N? zgG_3`_YZdT50>)(e`$~FK$}1& z)(Rn2!nfLQMHTs_HGq&xV$~d4YQUWOadBXs{6dcdXQRN`C~!8)Z)T&w5nDL)1c#pf zM(7Df8yIb1wE0c6fj2r}w1Ls)x6uYR(qOfL)#i8AW;G<7Pe9+2<#@zqqgGW) zd!H83GpPKGN5EztZ05mc-fx@E4;uYu&{+ID{ZGcunpi2&|bK5eR#4?4% za))C;9zpAI445L8$s$%-uK~ITJgwC+;DY!;2JwRpV!7e*a%VO`|1kb52L$MF{GI+` zxvw$E#-OawJ*+T2OaX!iK<)5L-{QGBTJK^o=9dpEEsJS?{^G*&7p-CO5|AZY&*GJo z0%kA7$DfU6b(7B3Up)VeOP4b^_SZ(Uy0KQ`io%Tar>w*y{UZ#$L|*Ym?~pQlY5(Ge z3?JWfJ(J^)rNnMMmSJ_e>Us3y16F4jDJfC^^Zr77>ve+lUb?Nbx#lS&lp^F8bAHd| z2Z=7e;n(vF_OM>eNmtJ+uc(xfewzHX9`uohgy5tEIP1G%@uMzN7<}LQ-TT&Rx^-HC z`xYG}2H80~J9~I|&@%xJhIsAOZaC69*eQa$o(p&k4EpdfG&C&y)2E1tsHn)u=&0D( z*o34nUsF>vGBdNXfMn(6<`#VWR)8odM10GyZcnZ3O|9z9ukSBt>@O-VE-x>yC@-z3 ztO9Mrs;fa$dD?evO-)^GO+!O{VNF|6ZF^B|2co_Q+#5<8yAh53%N;M9o0?mifwX{z zmo0^jNJP_MaqD1l%TQ(eU`_i#RWH7=eWam#xVZ<_(2H*B$F}rinvtW;NL&jN+kzZz z9T;r`GKgy$Ahr(R+XnINLxheYd&B3~F%vx)au0T*ZxkFUJ)_j# zQEDHK+K->?$4`NNe1ljbXk@=k^Fu%qrjUeb(4l^SG&4w=9wdP$vqNKZL*w(q6N@X| zrLSM55b^sCiq|?KD+Dy zeKX@gQ+P;$XB}HoT6%&)M#4r%`t1CI;62cKMxv$NcU;8%P!0Y%ThPa?FXL<3HaV( z4|L@e)3lMiQ~mChUX()k*Uu+QcAwm99n7Dy#&3rtoqnfMgc|Dxd1X&6<*y^fJ2b5+ z{DX$--eUWOl=0;A<=h|EhX`F6EU8pHyX~W9ND!`tj@&xO>3YpGS zw=-GvZW)P*ig4;0TpI+VM8d)XxTE|oUoh+x!cHMZA%timb$wRac5bvPvoE@BN1 z4uRd70T`&(pOxj*hHJk)Jw0DP^!M`e^6p;pIRw4c5Pf?N|F(7UU3!CGSfqbQR^Z3* zpsSj}u-bI`Ec{Kifpa@x`wq76{)T-AZ!yDL%ny-Vn#{VhvaoNX0KK~B4Er`jFkrRW2FBw;arQOh zJQu~!M@n#?li=0fE-o&~sV&KUUUK_Rskhy*+PJq-Jz%vV4-?3X|Ge73TRw0|01gSj zAptl54I63yJlenkXc%o^wE1ncfsHg+ZD6(eZMA{X238wbZD6&5eH+-f`R9Eb*hquX z21XkgZD6#4(dM5=8`!sj(FR7F-$onQNQ2b|R-4~e8yIb1wSm>Y^G*n&1etf2Lf z-3r^oGV4aQ&N7L?Dw;kCf|gHzKx>v+H`2eAAP4(vI*~P<1NGelb)AFFNOa2px^-~0 z14ZaT6MHbkUJS7tGv13G>&1d2xp$P@kDKVlt#DUR`tZ~t)W8UO2#rP!U;r5rhD5?m zkcrd@5)waw#8U=<5GY6jb$~EQqqQIc0n8TDfY}0YS_}din;9CL85{%r7T`IcxR^tY z(}*rmK*&HAhJj1~S)8CuP$^?n$~cueF-av)QYWaBp!F7Ia*8@NMV*|QoSvdgPf@3* zCue}nOo7%4v$NB4v$XySb8|pu=jUbtnbG|G?EL)P0__PXc&F98FD}f}NR7VJ8GYw6 z`g>d&t3Qh+-cHZqg0ixP@?i~!SDvpuFI>>l*7JSmrFFq8Nbj7Uc8DINA;2#rNH6pu zPET1?QBmuQ#unArTQxMa;@>%cfeH|XMa3nhWq|Xjs=B7Oj>dTeXiVDLJA_3Q6r}rh zOYiO(LJgz#jtq{-j}S&SZGw0i7+BdDSctQGH!N(B7L}AW*1E6Cz$MC~&~V_4(;r-r zxlWj!j|004(ZIMz{rIgz_ZjGQOJa6&u9nk984>|2lVN#nqA82?j80t9msfkTAUOFC zh#7JPli1wThaBiZ4)oP^BI`N_>bjA2J%dfX1C4#dO?{}QzTwt^k=6mQSg~zGxOSQl z+J}JL&mi79SiR2tfAX8GH4qaUk73!XK*u}G(z+E| z!ke5;1XWGdABxz?NixUiho=~vkhv72BNY%?y2fQ^o~Z^#JB-)IY79ciGuH&{zFAh% zQ<=K`?14KytWO-(V(J4_y=Qp@#mY8_Yxyz0(G;z?5Wipj!dmj@`1WY+z+LWfB{FZf zU#hp&%jF&Q*a(^K61EeDz`bIdVshAlLOOa5?igwus+fg?SH*|(W`Ob)aV}G>U8at5 z-QxQ>UP6dY7lTdv6Es5(9jY*>He@+Th+~B4+8pIxIt#AcJ63^AMgdU-z;%)r;e{q>CGFx=KG`=V|W~j6vwsH`MEW^70o3}f@_*3hgA?4 zv{emmO7jXp?j8V-yu3ZkeEh=U7;YcLw4-pHK!)+X7!r+v3rp_9j`fX#+dXvvOBxs* zL*gfgNWk=%1?C1YF~Evh&0spF->ly7Nzy%aTa?EZmTk3^`JQzj52wlwdk1OpXYW>B zPp`-+Da6hyNxz$u)uFh;;Dg1sb1tlm-KFcC#ZR!V)=HEAWiGPve$K^e$Rzju@(D!8 zr~H`4)tvh4Cs0&c_4CwKeD3nUTs8am-teziP0jpQ z?A64%|1-bClKivr#P=-+pEtg=EeHPq&=BNjsaBdAiTSEw**&*goH5Hnl*=128UFX+NC5E&{AuTDh(*{ z?@&03njBJ++OdXyHzdI-YRAtnqVMD=%&Dd0qs-#v${DQ<-K5{lw1J;DhQWuPg^6RM z$f!9#m+Ns)dV0F=J5g}d3646!Q71U+1d}_$imXQ2%;*^z+{Y%Ra z&!2sKYWvB`=99}^FE`USt``2U%>5jn1P0qy+PK7+f5^KXTxS?jXAxF^H>0z_H!$RL zYFzx6grt);3iY_Y~E3)ztSiRyB20H#F7tmNxd( zwDf@6a1<#ou)dSprKP*vMtS;t6S*D#_B18lgKwt?PuWOEm) zyKAti58K<1MfM|mk*L-|LN^N64w`a}jQ5O!l&@(NYGhz=aAr8Qp! z`5?4pu33;1LQCeFrFCFjY4-&lKswh9kU1a=%Xy3-af_B40tR^a&sc2#k6(p#3EB=d zjt>^Hva=oti}49pW?HM5_b$*lio@rR;nLk;CL_qW?ldzjGWTzFM_QcFP3fFjA=60ehL;yfD@ ze>whI?0si;>2uq(jGL=^DxZE{C+Eh`WT0Z{6R!9OE7*0|m{Tm*ejW;&Yro$g=U?Nn zdCk72i1;_3m6>y`5{6Ei$UBO*W=zBiU6pOoJ|2B0r0Mawc-x7nbz$`IbY)b%ZwWDz-Zvyzd4gX6fef z{3FlyT@5=Nn<8|I2ip6t-EFVJ6sK5g8w>;T}bQN~7(zEBxvD9_qKF5ks(J+)8e6_1gN6%i@yzm{b zE;8$!S81%;@y%5)#y_qZZ*8DkJJHx+@7hkt47Hdaj^46?e)4jpXO)>+Fz=O;F$Q+Y zVBrz&@}oxY*6BpW-f`>R=<|{HTtZq&u;Kfe;0?w}vFMM@=~slfuLv8VEv$|mU<$o= zEEfHgA;HOsRVECn93a`!Ib>lZ8YdO%ne@1H{o^Kqn1`do*#pl%7(5eyA7b&)S?d7% z(p8hVw67+r5mzga5ZiVqcc+8^-mAD|(Wh~x8~l;bhv{r4mvpXxds zlDYf-cDraL+1)#`rInv0AvlYT$_lSCrYN#+GB;=7MM(R{TAXF2gLt^w? zjJ(J1$3y)mkD{j(3W{2$ewH*>#ndm+pqfYMQuP8(lu$MIQsc$!ICSIL7jL3qJM z@yHsJ#wU&q$fzw=(0XnE$li)Mp^ASI0Tbc`~eU;_d(5YE}kur;jn>`TE7>s=60st2kXy{sQc zIJkRl5tRy2G;aP>-czA=xvJ^C^@a6SjoUci$u|vS5EtyTO7?4Op7P6~L@Pdt-8zkM zwIJHZ*zz;(72tw66sWOUujANsi>uMUsbcbtZjQ;%l5X?1C!HtZ>v5vQG0Sr)6z`isgatM!O3T4C#@fzvNk;b!c^PV zLd*8(X-B;?4yVsKoWJCxd(rXY6_*=2&KGq(bg#cMyy$rEyuHB{mz%n7*Y#iPnfP5Y z^f55>ere=waNFmW$vZPMKU*W8r)KZ0@A=*~54`v2!^6ix7FHqFFG3$Y4}bLHlj~g{ zchk489=?BX^YMY_7hBg@YnP}uenEbLAKkqZ{q4d-T_Rn5qut*m1bvKn8~nw~Kh--R z!_`0CCosb=BsP-n*}vlQwa zbrQ(z#N-lra%qA(Pnn#j(qw@;wE&(^PR&!O^AnRZQ`2*k)ALg_SpY}s49&0sz*RF6x za&4^i6u#nzcdNQp`F_j!C#OoaN7OIveb|QAk>^qMd^;;N+gyG&yo7gmRr_b~S-Ih~Ta=>X^py=a<^FAUhn;!)zx!4&yxs~y-$CWej8JISfSzq6$P+_~P z%vj{bd&q}f!Y{^1L;PU=Wj3!Fs(QGMe14PDhAUy?sscKXxm?~Fx+$En9(8Y2-E_?@ zrEkmer`SmU6PL6K*PeNIcO>w(sM4^tHQt`(d62%Zrg&_PMzr`7c8wb$HeDU)=P&!z z&@wlYE~B8eIt>i#Jx7t42v2fyl=YLk>5q=k-k6WCoS$CBgpZ1hGxD6F*cHbrU>b_! z)DV|rqm#>)4)upK=m@KDsqv(YCgn{+{W7sUu^QWUt*bN&v7*-!v#}h!tnu|DuLeZN zoe)>N)_c@vG)X;6Fd+fyAR|Y&ks_?JzQg2sTwJd8wQbS^Wosm!Galho=GIoBE9Lq| zyjt?5Kkk5O6faTjru2J#(U?5yMkzapr>E-p+L*xx+y>&FgmIsBELvvkM@NJ$zGy(Q zrrZ4R{%*F`eU_%u`{~1Wr9K(izCGatGbe)aC?j3TIweNA$DHxZH_2t{YC0(THK!m( z&OL654r~&@A8J))5HxSnJy5fynd=Clm`xOIPHjCYv=$*#yoXgRij&+K;Vj3XXw*w? zWP6l$W9Jg*Wb>WS7f-uLbocJIyY1{az<9QMjE>Fc1A(DSARc{bo6PpQCvP}*LH6sz zx}jOj=MDKE2ZExLwxBsocja(=F28^1`L%d21zggc@NsrAPeo3)e7DJ8*IMJU3yu(LOs>Ksf00g6)ESlZO+;Xsq}` z&aQAqKTHWPWpBmL_I_T%$wRTV=30HccOPr36#G?9J2`!OcIFBP0o{7m0{Zuid=($r zuWP+PuNjYgUZzg=toq3gni+PlO;NYFu1KnFKg8|3+fIp1@~+0IGR3#i@&?=Y zxs<9IAl@bK72i0klgCYVQ+t{tUSuv6>aQ)majYqbAJ<&EGo1_x=!>m666SC&h}#dp zTU2`2n>W6OsDm9PQQ}`KPoL)Z4eAoQv`uo)nFsgJzbTtX$=qt7-iO^Xx_^A35h?c4?dV)H)~*koTr6XN`( zm}emjjHR-_dLi>Y_v`MBWuW_on${ zUE29_-Ds&H_e$P`!f5r=ap(N2TB7P&{F1jxAC`$_j;7fz!`PG{_ExvA51Awtl>|Mq zHQDKK?`NJ#OMa^=<4vZEH-8;}?wW|NSC3jEzgzSL*V#+iv(zv9PXq*CS8AU$O^?#M zEpynLy`!uueWN7xqwW5g3cs(diC(p)y62zeT;SGBJAhb}u{35m=wt|m{P0l>~fXwP!IXDi?3t^AjHHtPurUfr>5Gbt?FOous6 z$pV{c<3(w%b0Ykgg#~m(L~d-}X0TEE&KAi#e6q&;JMM1X`(T^6ftaMBnEZX&y=Hs& z-jh>!D1Yq9$xWC3PuCTPGvZlVcys@mRwW7AYs;;xH zskgDUzo)scscoROYq+Bq-PVoi>cf_H&sX)%xAl+p4iMUr(YVP0+yaWQfFGMBj?WUwvm~0#ktTr5)0ptdiy#<7q0&Mw z;}bJu z%DLr;?KF?zI~Je7WA^i`gKwo8KA#R>^qGkmZHzeeWf5s_SjoNpRDg|M(KdI#9mdUZ zde!^o>}2Ha7cpEa#!Xw4pG$76dQ|QubkJ(LwNQ-3OXwIAi8Jp>$~qwhD@w)9h_ZD; zhab=P3Qm8y5oWOlx9gmf`Ww-cr$QypMJD3f^)6)voX(xZ^D9$yJ6{ZQKz?hUvyk+n z&+r{JN_w24qvkz+sM4L~kMly$s(N$YT88%>K2+7)`0Ulv*0a}bG}$rD@oHOd($`NN zVPJAtv6&`t{q;-V=D8teNnty&3;O*VZc?I=4jlWfLT#jK%IMj7E~$&K@#pasxrhYl z7eCjBIGuSo3%B<>OdO06^{@{EWx!Iw!b6_h!|cLcBv6t?9Hd4F@|Izi`s_e{sE>N1 zE|_i4IN7UcU~^nmO5B#bR)5$}=X#~#i|tLBEN5>x2#mZl{K~4tEM()O^!{pB`|UvU zGgrhJ`E((=50|bZ?|49C2S0~COp@G6=frDb9L|duzCw0g%nqaBaSlsWaWP~jMMWrUBu*wNu5R?6jlF^} zKDlUrgB?Tpp$6QS)G8xRin-?Dr-k=<6MDa%t6ja^r0Jt>*|i zIKgX4+2Nn6iFx?^HKfnvrjuX3P3@50$^fy*CL@|BuZ(2mFCHE%f+o5j1e2wNQ|LL= zb;nCL^35j~*z+LA%Y_~BV`YX_eB{cl-lx94Ba7H6((NqA@2S~!_MTaW#TFsA>LXM5 z)GC>!?9_Ud+=Pb>;_LM&^&drRV{BMd*huia(6r( zmB>Hy9G5vn3?;@-?3f!X;kz+S%-Hm0p8Q^{9>jZOh&GfKwLe}^J7l)5x}uW@6m$Qm z1_xSA&*0>JWwjW`2hL^wUP(?nm(j`l*{X{c1R@Ks8g2_oPT6*O* zRe*8(Ys+UgPoKT8 zdj8_2?F*Y1w$ESKS=-v%*xK8^w9mJ7wzYGxwR5z!cYNvKXz%2jB{zl6=t7nkJJmgGJ!xqYY9+isb4XJyx)mF3iijWpOu zgN-z?ZLr!rduHumXp@P1=`i46cE{nxCkOX9r{M)>Fe1yGQ*bV3rf%67_cp4B0`%&h zGpsiMyxPE#G&qt5N7CR(8f>J&X!Fmb4ZPh6qYaEUFxtRq1EbA9k2bJx1EUR$HouKF ztEeE~0mw)%HYQp-6;J8&Yy$C@P&<2QQ`6WQ5+`nF87td-7=>@^N zpq0|o-UaO?n83Tp)&5?g`j-{C8$lyl2GB3z$Jw9^pL5k^_*@V_=&`y|hF?%v0+?99 zdjRcH|4(~Y7ZODjg~!?TH;p1w*B=u6DJ^`7qBKHDvP@ITzeEsSH*;|{U1QBOH5Uzw zFbOnM5-VFl2`z)g(hAgiNVDuAP}6cPO)<8@Om^$snO*at@U9^EaPIrud*%%1JKsG& zbE4>F_v8jTlRp0L-WMfV730Ch!i zw4f<6XrAb8dS-?d2w7^$otNm1ytf6Be3)A8Q+?~D|FpZN*z>WVG9;EA*mUmsX`k= zJQeSWpb~#sr+>vzfO>qNt^fY5ae;l>pwO70C)(f)U5K|=Xu+ef%Auoe-y^~fMI@^t zo(@Nb{+o{)Gssb^jsAD0rVOi4K@F=Hh1T1jtxYIbdE&W+TE zzfZ~Q(vk&f@*1g!hgAGnswkAU>C@{6WMq}fGU{bz>dc*rEN`Ez@cit1qw>lY#V-3? zh5Rg*@n1agVRZDPPNx}}(tOiTIB%O!9r!dg`&nH!H97fZuy(ptG2P!at?il7YRJ3} zShKBPVd?cSO6-TGckX`d9IOT1&w2ek%tRe;U+9d}y{piTHN(tPKp?y)Vy`9kTCTR) z;5`uuFA`q#Hdv30^~gxlutgc~Hk#2jceg>3h9nJ18j>_5X-LwLq#;Q|lD76p8coWF zG>nnar57^Km7LC3o+&KNDJsh?DbFjta=!d(@uf>3YB)nb#$a literal 0 HcmV?d00001 diff --git a/blazor/file-upload/images/blazor-fileupload-valuechange.gif b/blazor/file-upload/images/blazor-fileupload-valuechange.gif new file mode 100644 index 0000000000000000000000000000000000000000..afd16653d845284313f2e32d993ecb95324ef1de GIT binary patch literal 175137 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 literal 0 HcmV?d00001 From 4e0fa1f841690ee991716e5378cbebf779fd892b Mon Sep 17 00:00:00 2001 From: PriyankaKarthikeyan14 Date: Fri, 17 Oct 2025 16:58:26 +0530 Subject: [PATCH 2/2] Docs(BLAZ-982992): Revamp FileUpload documentation(file-upload/getting-started-with-maui-app.md, getting-started-with-server-app.md, getting-started-with-web-app.md, getting-started.md) --- .../file-upload/getting-started-with-maui-app.md | 12 ------------ .../getting-started-with-server-app.md | 12 ------------ .../file-upload/getting-started-with-web-app.md | 16 ---------------- blazor/file-upload/getting-started.md | 12 ------------ 4 files changed, 52 deletions(-) diff --git a/blazor/file-upload/getting-started-with-maui-app.md b/blazor/file-upload/getting-started-with-maui-app.md index 1046e07260..789abeef57 100644 --- a/blazor/file-upload/getting-started-with-maui-app.md +++ b/blazor/file-upload/getting-started-with-maui-app.md @@ -176,8 +176,6 @@ N> If you encounter any errors while using the Android Emulator, refer to the fo {% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" backgroundimage "[Blazor FileUpload Component](./images/blazor-fileupload-component.png)" %} -#### Preview - * Press Ctrl+F5 (Windows) or +F5 (macOS) to launch the application. This will render the Syncfusion® Blazor File Upload component in your default web browser. {% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" backgroundimage "[Blazor FileUpload Component](./images/blazor-fileupload-component.png)" %} @@ -241,8 +239,6 @@ This example demonstrates how to use the [`ValueChange`](https://help.syncfusion N> When saving files directly in a Blazor Server application using [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) and [`AutoUpload`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.SfUploader.html#Syncfusion_Blazor_Inputs_SfUploader_AutoUpload), the files are saved on the server where the Blazor Server app is running, not on the client's machine. You need appropriate file system permissions for the server process to write to the specified directory. Also, ensure the target directory (`wwwroot/uploads` in this example) exists or is created programmatically. In a production environment, consider secure storage solutions for uploaded files. -### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/hDVyZkrqBvaSlvht?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ![Blazor File Upload ValueChange Event](images/blazor-fileupload-valuechange.gif) @@ -315,12 +311,8 @@ This example demonstrates how to read the content of an uploaded file into a [Me N> When using `MemoryStream` for large files, be mindful of server memory consumption. If you expect very large files, consider processing them in chunks or saving them to temporary storage before processing to avoid out-of-memory exceptions. The `long.MaxValue` in `OpenReadStream` indicates the maximum buffer size. In a Blazor Server app, `Stream` operations occur on the server. -#### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/BjreNaLUhdwxzvHS?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -#### Gif - ![Blazor File Upload Memory Stream Example](images/blazor-fileupload-memorystream.gif) @@ -360,8 +352,6 @@ This example shows how to use the [`Created`](https://help.syncfusion.com/cr/bla N> The [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event is useful for client-side JavaScript interop if you need to manipulate the DOM elements of the uploader component immediately after it's ready. However, for most Blazor-specific customizations (like custom templates), you should use the built-in Blazor features. -### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/VtLyNuVUBGtPZrdo?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ![Blazor File Upload Created Example](images/blazor-fileupload-created.gif) @@ -417,8 +407,6 @@ This example demonstrates how to use the [FileSelected Event](https://help.syncf N> Setting `args.Cancel = true` in the `FileSelected` event will prevent the file (or files if `args.Files` contains multiple) from being added to the uploader's internal file list. This is a client-side validation and should be complemented with server-side validation for robust security and data integrity. -### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/BDLIZuBUVwEJoJpz?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ![Blazor File Upload File Selected Example](images/blazor-fileupload-fileselected.gif) diff --git a/blazor/file-upload/getting-started-with-server-app.md b/blazor/file-upload/getting-started-with-server-app.md index fc321f86b7..77619e06ce 100644 --- a/blazor/file-upload/getting-started-with-server-app.md +++ b/blazor/file-upload/getting-started-with-server-app.md @@ -217,8 +217,6 @@ N> If an Interactivity Location is set to `Global` and the **Render Mode** is se {% endhighlight %} {% endtabs %} -#### Preview - * Press Ctrl+F5 (Windows) or +F5 (macOS) to launch the application. This will render the Syncfusion® Blazor File Upload component in your default web browser. {% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} @@ -282,8 +280,6 @@ This example demonstrates how to use the [`ValueChange`](https://help.syncfusion N> When saving files directly in a Blazor Server application using [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) and [`AutoUpload`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.SfUploader.html#Syncfusion_Blazor_Inputs_SfUploader_AutoUpload), the files are saved on the server where the Blazor Server app is running, not on the client's machine. You need appropriate file system permissions for the server process to write to the specified directory. Also, ensure the target directory (`wwwroot/uploads` in this example) exists or is created programmatically. In a production environment, consider secure storage solutions for uploaded files. -### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/hDVyZkrqBvaSlvht?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ![Blazor File Upload ValueChange Event](images/blazor-fileupload-valuechange.gif) @@ -356,12 +352,8 @@ This example demonstrates how to read the content of an uploaded file into a [Me N> When using `MemoryStream` for large files, be mindful of server memory consumption. If you expect very large files, consider processing them in chunks or saving them to temporary storage before processing to avoid out-of-memory exceptions. The `long.MaxValue` in `OpenReadStream` indicates the maximum buffer size. In a Blazor Server app, `Stream` operations occur on the server. -#### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/BjreNaLUhdwxzvHS?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -#### Gif - ![Blazor File Upload Memory Stream Example](images/blazor-fileupload-memorystream.gif) @@ -401,8 +393,6 @@ This example shows how to use the [`Created`](https://help.syncfusion.com/cr/bla N> The [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event is useful for client-side JavaScript interop if you need to manipulate the DOM elements of the uploader component immediately after it's ready. However, for most Blazor-specific customizations (like custom templates), you should use the built-in Blazor features. -### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/VtLyNuVUBGtPZrdo?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ![Blazor File Upload Created Example](images/blazor-fileupload-created.gif) @@ -458,8 +448,6 @@ This example demonstrates how to use the [FileSelected Event](https://help.syncf N> Setting `args.Cancel = true` in the `FileSelected` event will prevent the file (or files if `args.Files` contains multiple) from being added to the uploader's internal file list. This is a client-side validation and should be complemented with server-side validation for robust security and data integrity. -### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/BDLIZuBUVwEJoJpz?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ![Blazor File Upload File Selected Example](images/blazor-fileupload-fileselected.gif) diff --git a/blazor/file-upload/getting-started-with-web-app.md b/blazor/file-upload/getting-started-with-web-app.md index d2338a7ffa..ee28c98730 100644 --- a/blazor/file-upload/getting-started-with-web-app.md +++ b/blazor/file-upload/getting-started-with-web-app.md @@ -257,12 +257,6 @@ N> If an **Interactivity Location** is set to `Global` and the **Render Mode** i {% endhighlight %} {% endtabs %} -* Press Ctrl+F5 (Windows) or +F5 (macOS) to launch the application. This will render the Syncfusion® Blazor File Upload component in your default web browser. - -{% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} - -#### Preview - * Press Ctrl+F5 (Windows) or +F5 (macOS) to launch the application. This will render the Syncfusion® Blazor File Upload component in your default web browser. {% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} @@ -326,8 +320,6 @@ This example demonstrates how to use the [`ValueChange`](https://help.syncfusion N> When saving files directly in a Blazor Server application using [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) and [`AutoUpload`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.SfUploader.html#Syncfusion_Blazor_Inputs_SfUploader_AutoUpload), the files are saved on the server where the Blazor Server app is running, not on the client's machine. You need appropriate file system permissions for the server process to write to the specified directory. Also, ensure the target directory (`wwwroot/uploads` in this example) exists or is created programmatically. In a production environment, consider secure storage solutions for uploaded files. -### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/hDVyZkrqBvaSlvht?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ![Blazor File Upload ValueChange Event](images/blazor-fileupload-valuechange.gif) @@ -400,12 +392,8 @@ This example demonstrates how to read the content of an uploaded file into a [Me N> When using `MemoryStream` for large files, be mindful of server memory consumption. If you expect very large files, consider processing them in chunks or saving them to temporary storage before processing to avoid out-of-memory exceptions. The `long.MaxValue` in `OpenReadStream` indicates the maximum buffer size. In a Blazor Server app, `Stream` operations occur on the server. -#### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/BjreNaLUhdwxzvHS?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -#### Gif - ![Blazor File Upload Memory Stream Example](images/blazor-fileupload-memorystream.gif) @@ -445,8 +433,6 @@ This example shows how to use the [`Created`](https://help.syncfusion.com/cr/bla N> The [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event is useful for client-side JavaScript interop if you need to manipulate the DOM elements of the uploader component immediately after it's ready. However, for most Blazor-specific customizations (like custom templates), you should use the built-in Blazor features. -### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/VtLyNuVUBGtPZrdo?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ![Blazor File Upload Created Example](images/blazor-fileupload-created.gif) @@ -502,8 +488,6 @@ This example demonstrates how to use the [FileSelected Event](https://help.syncf N> Setting `args.Cancel = true` in the `FileSelected` event will prevent the file (or files if `args.Files` contains multiple) from being added to the uploader's internal file list. This is a client-side validation and should be complemented with server-side validation for robust security and data integrity. -### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/BDLIZuBUVwEJoJpz?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ![Blazor File Upload File Selected Example](images/blazor-fileupload-fileselected.gif) diff --git a/blazor/file-upload/getting-started.md b/blazor/file-upload/getting-started.md index 0d973d53db..4b2ec2aca6 100644 --- a/blazor/file-upload/getting-started.md +++ b/blazor/file-upload/getting-started.md @@ -212,8 +212,6 @@ N> If an Interactivity Location is set to `Global` and the **Render Mode** is se {% endhighlight %} {% endtabs %} -### Preview - * Press Ctrl+F5 (Windows) or +F5 (macOS) to launch the application. This will render the Syncfusion® Blazor File Upload component in your default web browser. {% previewsample "https://blazorplayground.syncfusion.com/embed/LXBJXsrOqbMEOurR?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} @@ -277,8 +275,6 @@ This example demonstrates how to use the [`ValueChange`](https://help.syncfusion N> When saving files directly in a Blazor Server application using [`ValueChange`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) and [`AutoUpload`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.SfUploader.html#Syncfusion_Blazor_Inputs_SfUploader_AutoUpload), the files are saved on the server where the Blazor Server app is running, not on the client's machine. You need appropriate file system permissions for the server process to write to the specified directory. Also, ensure the target directory (`wwwroot/uploads` in this example) exists or is created programmatically. In a production environment, consider secure storage solutions for uploaded files. -### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/hDVyZkrqBvaSlvht?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ![Blazor File Upload ValueChange Event](images/blazor-fileupload-valuechange.gif) @@ -351,12 +347,8 @@ This example demonstrates how to read the content of an uploaded file into a [Me N> When using `MemoryStream` for large files, be mindful of server memory consumption. If you expect very large files, consider processing them in chunks or saving them to temporary storage before processing to avoid out-of-memory exceptions. The `long.MaxValue` in `OpenReadStream` indicates the maximum buffer size. In a Blazor Server app, `Stream` operations occur on the server. -#### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/BjreNaLUhdwxzvHS?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -#### Gif - ![Blazor File Upload Memory Stream Example](images/blazor-fileupload-memorystream.gif) @@ -396,8 +388,6 @@ This example shows how to use the [`Created`](https://help.syncfusion.com/cr/bla N> The [`Created`](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_Created) event is useful for client-side JavaScript interop if you need to manipulate the DOM elements of the uploader component immediately after it's ready. However, for most Blazor-specific customizations (like custom templates), you should use the built-in Blazor features. -### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/VtLyNuVUBGtPZrdo?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ![Blazor File Upload Created Example](images/blazor-fileupload-created.gif) @@ -453,8 +443,6 @@ This example demonstrates how to use the [FileSelected Event](https://help.syncf N> Setting `args.Cancel = true` in the `FileSelected` event will prevent the file (or files if `args.Files` contains multiple) from being added to the uploader's internal file list. This is a client-side validation and should be complemented with server-side validation for robust security and data integrity. -### Preview - {% previewsample "https://blazorplayground.syncfusion.com/embed/BDLIZuBUVwEJoJpz?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ![Blazor File Upload File Selected Example](images/blazor-fileupload-fileselected.gif)