Mostrare un’anteprima di un file PDF può sempre essere comodo, magari per fare un convertitore online o qualcosa del genere. Oggi andiamo a costruire un componente che ci permetta di visualizzare i file PDF ed usarlo nelle pagine Blazor.
Creazione del progetto
Apriamo Visual Studio 2022 (o qualsiasi IDE utilizziate) e creaiamo un nuovo progetto Blazor WebAssembly, scegliete voi se spuntare ASP.NET Core Hosted (nel codice di esempio che vedremo io l’ho fatto), e avremo 2 progetti per il Client e il Server (o solo per il client in caso non aveste spuntato la casella prima). Ora andate sulla soluzione e aggiungete un nuovo progetto e selezionate Libreria di classi Razor
, dategli un nome e poi Visual Studio penserà alla creazione di questo nuovo progetto, che alla fine sarà il nostro componente: PdfViewer.
Sviluppo della libreria
Ora che abbiamo creato il nostro progetto andiamo ad aggiungere un nuovo componente Razor che si chiamerà PdfViewer
. Creiamo il codice dietro al nostro componente (potete utilizzare anche il formato in un unico file tramite @code
, a me torna più comodo avere il codice in file separati), ora il nostro componente dovrà avere dei parametri da passare che sono:
- Base64Data: è la stringa in formato base64 che utilizzeremo per mostrare il file (più avanti nell’articolo vi mostrerò anche come ottenere questa stringa)
- Width: la larghezza del componente
- Height: l’altezza del componente
public partial class PdfViewer
{
[Parameter]
public string? Base64Data { get; set; }
[Parameter]
public string? Width { get; set; } // 200px o 20%
[Parameter]
public string? Height { get; set; } // 200px o 20%
}
Vi starete chiedendo ma perché l’altezza e la larghezza sono state dichiarate come stringhe? Perché nel nostro file .razor in cui definiremo la struttura del componente utilizzeremo un div a cui gli passeremo le 2 dimensioni sotto forma di stringa così da poterci sbizzarrire con le unità di misura etc.
Ora andiamo a scrivere il codice HTML del nostro componente.
<div style="height: @Height; width: @Width">
@if (!string.IsNullOrEmpty(Base64Data))
{
}
else
{
<div>Nessun file selezionato</div>
}
</div>
Per mostrare il file PDF utilizziamo il tag iframe
nel quale all’attributo src
passiamo la nostra stringa in formato base64, in più mettiamo un controllo sulla stringa Base64Data
per controllare che non sia nulla oppure vuota, nel caso fosse vuota mostriamo un semplice testo Nessun file selezionato
. E con questo abbiamo finito di scrivere il nostro componente, ora andiamo a provarlo.
Aggiungiamo dei metodi di estensione
Dato che stiamo scrivendo una libreria è buona pratica aggiungere dei metodi che possano aiutare ad utilizzarla, per questo ora andremo a creare dei metodi di estensione per convertire uno Stream
in una stringa in formato Base64 e per convertire uno Stream
qualsiasi (che ricordiamo essere una classa astratta quindi può avere diverse istanze di diverso tipo) in un MemoryStream
. Creiamo una cartella con il nome Extensions e vi aggiungiamo una classe statica che chiameremo StreamExtensions.
public static class StreamExtensions
{
/// <summary>
/// Allows to convert a stream to a base64 encoded string
/// </summary>
/// <param name="stream">The stream to convert</param>
/// <returns>Base64 encoded string</returns>
public static string ToBase64String(this Stream stream)
{
if (stream is MemoryStream memoryStream)
{
return Convert.ToBase64String(memoryStream.ToArray());
}
var bytes = new Byte[(int)stream.Length];
stream.ReadAsync(bytes, 0, (int)stream.Length);
return Convert.ToBase64String(bytes);
}
public static async Task<MemoryStream> ToMemoryStreamAsync(this Stream stream)
{
var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream);
memoryStream.Position = 0;
return memoryStream;
}
}
Questi 2 metodi ci ritorneranno utili quando andremo a testare il nostro componente e per far in modo di avere una stringa base64.
Testiamo il nostro componente
Andiamo nel progetto .Client di Blazor (la parte di Front-End) e aggiungiamo il riferimento alla nostra libreria, altrimenti non riusciremo ad utilizzarlo. Ora nel file _Imports.razor aggiungiamo le using al nostro componente e agli extension method, così da averle importate in tutto il progetto, ed evitiamo di scrivere ogni volta le using.
@using CA.Blazor.Pdf;
@using CA.Blazor.Pdf.Extensions;
Ora andiamo nella pagina Index.razor e aggiungiamo un file picker e il nostro componente.
<InputFile OnChange="OnInputFileChange" />
<PdfViewer Base64Data="@Base64" Height="400px" Width="50%"/>
@code {
public string Base64 { get; set; }
IBrowserFile selectedFiles;
private async void OnInputFileChange(InputFileChangeEventArgs e)
{
selectedFiles = e.File;
var memory = await selectedFiles.OpenReadStream().ToMemoryStreamAsync();
Base64 = memory.ToBase64String();
this.StateHasChanged();
}
}
Blazor ci mette a disposizione un componente InputFile
che ci permette di selezionare un file dalla nostra macchina (per ora non impostiamo nessun filtro sui file), e vuole un metodo per gestire il cambio del file e sarà quello che noi chiamiamo OnInputFileChange
.
Il file che andremo a selezionare è di tipo IBrowserFile
, che per fortuna ha un metodo OpenReadStream()
che inserisce il file in uno stream, però ancora ci manca un passaggio, a noi ci serve un MemoryStream
, ed ecco che ci accorre in soccorso uno dei 2 metodi che abbiamo scritto in precedenza, ora che abbiamo tutto pronto bisogna solo convertire l’oggetto MemoryStream
in una stringa base64, ed ancora una volta ci torna in soccorso un altro degli extension method. Una volta fatto tuttò ciò chiamiamo il metodo StateHasChanged()
per assicurarci che le modifiche siano renderizzate e vedrete che avrete un’anteprima del vostro file PDF.
Conclusioni
Siamo giunti alla fine di questo articolo, ed abbiamo imparato a sviluppare un componente che possa mostrare i file PDF in anteprima, ed in più abbiamo reso disponibili agli sviluppatori degli extension method per aiutarlo a ricavare il formato base64 del file da visualizzare. Il codice che è presente sia su GitHub come repository, e potrete anche scaricarvi la libreria tramite NuGet.
Vi ringrazio a tutti e Happy Blazor Coding!