Uploading Files in .NET MAUI
This tutorial guides you through implementing file upload functionality in your .NET MAUI applications. We'll cover how to select files and send them to a server.
Introduction
.NET MAUI enables you to build cross-platform native applications for Windows, macOS, iOS, and Android from a single codebase. File uploading is a common requirement in many applications, allowing users to share data, submit forms, or upload media.
Prerequisites
- .NET 6 or later installed.
- Visual Studio 2022 with the .NET MAUI workload.
- A basic understanding of C# and .NET MAUI development.
- A backend service to receive file uploads (e.g., ASP.NET Core Web API).
Steps to Implement File Upload
1. File Selection
You'll need a way for users to select files from their device. .NET MAUI doesn't have a built-in file picker control, but you can use community libraries or platform-specific implementations.
A popular and recommended approach is to use the CommunityToolkit.Maui.MauiExtensions library, which provides a convenient FilePickerService
.
Installing the NuGet Package
dotnet add package CommunityToolkit.Maui.MauiExtensions
Using the File Picker Service
In your MAUI application's MauiProgram.cs
, register the service:
using CommunityToolkit.Maui.Markup;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFamily("OpenSans-Regular.ttf");
fonts.AddFamily("OpenSans-Semibold.ttf");
})
.Services.AddSingleton<IFilePickerService, FilePickerService>();
return builder.Build();
}
}
Then, inject the service into your ViewModel or Page and use it to pick a file:
using CommunityToolkit.Maui.Core;
using CommunityToolkit.Maui.Models;
// In your ViewModel or Page
public class UploadViewModel : ObservableObject
{
private readonly IFilePickerService _filePickerService;
[ObservableProperty]
private string selectedFileName;
public UploadViewModel(IFilePickerService filePickerService)
{
_filePickerService = filePickerService;
}
[RelayCommand]
public async Task PickFileAsync()
{
var result = await _filePickerService.PickFileAsync();
if (result != null)
{
SelectedFileName = $"Selected: {result.FileName}";
// You'll need to store the file stream or path for uploading
}
}
}
2. Uploading the File
Once you have the selected file's stream or path, you can upload it to your server using HttpClient
.
Example UI for File Upload
In your XAML:
<!-- In your ContentPage.xaml -->
<StackLayout Padding="20" Spacing="10">
<Button Text="Select File" Command="{Binding PickFileCommand}" />
<Label Text="{Binding SelectedFileName}" />
<Button Text="Upload File" Command="{Binding UploadFileCommand}" />
<!-- Optional: Progress Bar -->
<StackLayout x:Name="progressBarContainer" IsVisible="False">
<ProgressBar Progress="{Binding UploadProgress}" />
<Label Text="{Binding UploadProgressText}" />
</StackLayout>
</StackLayout>
Uploading Logic in ViewModel
Extend your ViewModel to handle the upload:
using System.Net.Http;
using System.IO;
using System.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Maui.Models;
using System.Threading.Tasks;
public partial class UploadViewModel : ObservableObject
{
// ... existing properties and constructor ...
[ObservableProperty] private double uploadProgress;
[ObservableProperty] private string uploadProgressText;
[RelayCommand]
public async Task UploadFileAsync()
{
if (PickedFile == null) return;
var httpClient = new HttpClient();
var url = "YOUR_SERVER_UPLOAD_URL"; // e.g., "https://yourserver.com/api/upload"
using (var formData = new MultipartFormDataContent())
{
var fileContent = new StreamContent(PickedFile.GetStream());
fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data")
{
Name = "file"; // The name of the form field
FileName = PickedFile.FileName;
}
formData.Add(fileContent, "file", PickedFile.FileName);
try
{
UploadProgressText = "Uploading...";
var response = await httpClient.PostAsync(url, formData, new CancellationToken());
if (response.IsSuccessStatusCode)
{
SelectedFileName = "Upload successful!";
UploadProgress = 1.0;
UploadProgressText = "";
}
else
{
SelectedFileName = $"Upload failed: {response.StatusCode}";
UploadProgress = 0;
UploadProgressText = "";
}
}
catch (Exception ex)
{
SelectedFileName = $"Error: {ex.Message}";
UploadProgress = 0;
UploadProgressText = "";
}
}
}
}
3. Handling Progress (Optional)
For larger files, it's good practice to show upload progress. You can achieve this by creating a custom StreamContent
that reports progress.
StreamContent
implementation that wraps the file stream and reports bytes sent.
4. Backend Considerations
Your server-side application (e.g., ASP.NET Core Web API) needs to be configured to accept file uploads. This typically involves:
- Creating an API endpoint that accepts
POST
requests. - Using
IFormFile
to bind the uploaded file. - Saving the file to a designated directory on the server.
- Handling potential errors like large file sizes or invalid file types.
Example ASP.NET Core Web API Controller Action
using Microsoft.AspNetCore.Mvc;
using System.IO;
using System.Threading.Tasks;
[Route("api/[controller]")]
[ApiController]
public class UploadController : ControllerBase
{
[HttpPost]
public async Task<IActionResult> UploadFile(IFormFile file)
{
if (file == null || file.Length == 0)
return BadRequest("No file uploaded.");
var uploadsFolder = Path.Combine(Directory.GetCurrentDirectory(), "Uploads");
if (!Directory.Exists(uploadsFolder))
Directory.CreateDirectory(uploadsFolder);
var filePath = Path.Combine(uploadsFolder, file.FileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return Ok(new { message = $"File '{file.FileName}' uploaded successfully." });
}
}
Conclusion
By following these steps, you can effectively implement file upload functionality in your .NET MAUI applications, enhancing user interaction and data sharing capabilities.
YOUR_SERVER_UPLOAD_URL
with the actual URL of your backend upload endpoint.
For more advanced scenarios, such as handling multiple files, resuming interrupted uploads, or implementing security measures, explore the capabilities of your chosen backend framework and consider specialized libraries.