Partager via


Afficher des images et des documents dans Blazor ASP.NET Core

Remarque

Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 9 de cet article.

Avertissement

Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la stratégie de support .NET et .NET Core. Pour la version actuelle, consultez la version .NET 9 de cet article.

Important

Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.

Pour la version actuelle, consultez la version .NET 9 de cet article.

Cet article décrit les approches d’affichage d’images et de documents dans les applications Blazor.

Les exemples de cet article sont disponibles pour l’inspection et l’utilisation dans les exemples d’applications Blazor :

Référentiel GitHub dotnet/blazor-samples : accédez à l’application nommée BlazorSample_BlazorWebApp (8.0 ou version ultérieure), BlazorSample_Server (7.0 ou version antérieure) ou BlazorSample_WebAssembly.

Définition dynamique d’une source d’image

L’exemple suivant montre comment définir dynamiquement la source d’une image avec un champ C#.

L’exemple de cette section utilise trois fichiers image, nommés image1.png, image2.png et image3.png. Placez les images dans un nouveau dossier nommé images à la racine web de l’application (wwwroot). Le dossier images est utilisé uniquement à des fins de démonstration. Vous pouvez organiser les actifs statiques dans n’importe quelle disposition de dossier de votre choix et servir les actifs directement à partir du dossier wwwroot.

Dans le composant ShowImage1 suivant :

  • La source de l’image (src) est définie dynamiquement sur la valeur de imageSource en C#.
  • La méthode ShowImage met à jour le champ imageSource en fonction d’un argument d’image id passé à la méthode.
  • Les boutons de rendu appellent la méthode ShowImage avec un argument image pour chacune des trois images disponibles dans le dossier images. Le nom de fichier est composé à l’aide de l’argument passé à la méthode et correspond à l’une des trois images du dossier images.

ShowImage1.razor:

@page "/show-image-1"

<PageTitle>Show Image 1</PageTitle>

<h1>Show Image Example 1</h1>

@if (imageSource is not null)
{
    <p>
        <img src="@imageSource" />
    </p>
}

@for (var i = 1; i <= 3; i++)
{
    var imageId = i;
    <button @onclick="() => ShowImage(imageId)">
        Image @imageId
    </button>
}

@code {
    private string? imageSource;

    private void ShowImage(int id) => imageSource = $"images/image{id}.png";
}
@page "/show-image-1"

<PageTitle>Show Image 1</PageTitle>

<h1>Show Image Example 1</h1>

@if (imageSource is not null)
{
    <p>
        <img src="@imageSource" />
    </p>
}

@for (var i = 1; i <= 3; i++)
{
    var imageId = i;
    <button @onclick="() => ShowImage(imageId)">
        Image @imageId
    </button>
}

@code {
    private string? imageSource;

    private void ShowImage(int id) => imageSource = $"images/image{id}.png";
}
@page "/show-image-1"

<h1>Dynamic Image Source Example</h1>

@if (imageSource is not null)
{
    <p>
        <img src="@imageSource" />
    </p>
}

@for (var i = 1; i <= 3; i++)
{
    var imageId = i;
    <button @onclick="() => ShowImage(imageId)">
        Image @imageId
    </button>
}

@code {
    private string? imageSource;

    private void ShowImage(int id)
    {
        imageSource = $"images/image{id}.png";
    }
}
@page "/show-image-1"

<h1>Dynamic Image Source Example</h1>

@if (imageSource is not null)
{
    <p>
        <img src="@imageSource" />
    </p>
}

@for (var i = 1; i <= 3; i++)
{
    var imageId = i;
    <button @onclick="() => ShowImage(imageId)">
        Image @imageId
    </button>
}

@code {
    private string? imageSource;

    private void ShowImage(int id)
    {
        imageSource = $"images/image{id}.png";
    }
}

L’exemple précédent utilise un champ C# pour contenir les données sources de l’image, mais vous pouvez également utiliser une propriété C# pour contenir les données.

Évitez d’utiliser une variable de boucle directement dans une expression lambda, comme i dans l’exemple de boucle for précédent. Sinon, la même variable est utilisée par toutes les expressions lambda, ce qui entraîne l’utilisation de la même valeur dans toutes les expressions lambda. Capturez la valeur de la variable dans une variable locale. Dans l'exemple précédent :

  • La variable de boucle i est affectée à imageId.
  • imageId est utilisé dans l’expression lambda.

Vous pouvez également utiliser une boucle foreach avec Enumerable.Range, qui ne souffre pas du problème précédent :

@foreach (var imageId in Enumerable.Range(1, 3))
{
    <button @onclick="() => ShowImage(imageId)">
        Image @imageId
    </button>
}

Pour plus d’informations sur les expressions lambda avec gestion des événements, consultez Gestion des événements Blazor ASP.NET Core.

Diffuser en continu des données d’image ou de document

Une image ou un autre type de document, comme un PDF, peuvent être envoyés directement au client à l’aide des fonctions d’interopérabilité de diffusion en continu de Blazor au lieu d’héberger l’image sur une URL publique.

L’exemple de cette section diffuse en continu des données sources à l’aide de l’interopérabilité JavaScript (JS). La fonction setSourceJS suivante :

  • Peut être utilisé pour diffuser en continu du contenu pour les éléments suivants : <body>, <embed>, <iframe>, <img>, <link>, <object>, <script>, <style> et <track>.
  • Accepte un élément id pour afficher le contenu du fichier, un flux de données pour le document, le type de contenu et un titre pour l’élément d’affichage.

La fonction  :

  • Lit le flux fourni dans un ArrayBuffer.
  • Crée un Blob pour envelopper le ArrayBuffer, en définissant le type de contenu du blob.
  • Crée une URL d’objet pour servir d’adresse au document à afficher.
  • Définissez le titre de l’élément (title) à partir du title paramètre et définit la source de l’élément (src) à partir de l’URL de l’objet créé.
  • Pour éviter les fuites de mémoire, la fonction appelle revokeObjectURL à supprimer l’URL de l’objet une fois que l’élément charge la ressource (événement load).
<script>
  window.setSource = async (elementId, stream, contentType, title) => {
    const arrayBuffer = await stream.arrayBuffer();
    let blobOptions = {};
    if (contentType) {
      blobOptions['type'] = contentType;
    }
    const blob = new Blob([arrayBuffer], blobOptions);
    const url = URL.createObjectURL(blob);
    const element = document.getElementById(elementId);
    element.title = title;
    element.onload = () => {
      URL.revokeObjectURL(url);
    }
    element.src = url;
  }
</script>

Remarque

Pour obtenir une aide générale sur l’emplacement deJS et nos recommandations pour les applications de production, consultez Emplacement de JavaScript dans les applications Blazor ASP.NET Core.

Le composant ShowImage2 suivant :

  • Injecte des services pour un System.Net.Http.HttpClient et Microsoft.JSInterop.IJSRuntime.
  • Inclut une balise <img> pour afficher une image.
  • Dispose d’une méthode C# GetImageStreamAsync pour récupérer un Stream pour une image. Une application de production peut générer dynamiquement une image en fonction de l’utilisateur spécifique ou récupérer une image à partir du stockage. L’exemple suivant récupère l’avatar .NET pour le dépôt GitHub dotnet.
  • Dispose d’une méthode SetImageAsync qui est déclenchée lors de la sélection du bouton par l’utilisateur. SetImageAsync effectue les tâches suivantes :
    • Récupère le Stream partir de GetImageStreamAsync.
    • Encapsule le Stream dans un DotNetStreamReference, ce qui permet de diffuser en continu les données d’image vers le client.
    • Appelle la fonction JavaScript setSource, qui accepte les données sur le client.

Remarque

Les applications côté serveur utilisent un service HttpClient dédié pour effectuer des requêtes. Par conséquent, aucune action n’est requise par le développeur d’une application Blazor côté serveur pour inscrire un service HttpClient. Les applications côté client ont une inscription de service HttpClient par défaut lorsque l’application est créée à partir d’un modèle de projet Blazor. Si aucune inscription de service HttpClient n’est présente dans le fichier Program d’une application côté client, fournissez-en une en ajoutant builder.Services.AddHttpClient();. Pour plus d’informations, consultez Effectuer des requêtes HTTP en utilisant IHttpClientFactory dans ASP.NET Core.

ShowImage2.razor:

@page "/show-image-2"
@inject HttpClient Http
@inject IJSRuntime JS

<PageTitle>Show Image 2</PageTitle>

<h1>Show Image Example 2</h1>

<button @onclick="SetImageAsync">
    Set Image
</button>

<div class="p-3">
    <img id="avatar" />
</div>

@code {
    private async Task<Stream> GetImageStreamAsync() => 
        await Http.GetStreamAsync("https://avatars.githubusercontent.com/u/9141961");

    private async Task SetImageAsync()
    {
        var imageStream = await GetImageStreamAsync();
        var strRef = new DotNetStreamReference(imageStream);
        await JS.InvokeVoidAsync("setSource", "avatar", strRef, "image/png", 
            ".NET GitHub avatar");
    }
}
@page "/show-image-2"
@inject HttpClient Http
@inject IJSRuntime JS

<PageTitle>Show Image 2</PageTitle>

<h1>Show Image Example 2</h1>

<button @onclick="SetImageAsync">
    Set Image
</button>

<div class="p-3">
    <img id="avatar" />
</div>

@code {
    private async Task<Stream> GetImageStreamAsync() => 
        await Http.GetStreamAsync("https://avatars.githubusercontent.com/u/9141961");

    private async Task SetImageAsync()
    {
        var imageStream = await GetImageStreamAsync();
        var strRef = new DotNetStreamReference(imageStream);
        await JS.InvokeVoidAsync("setSource", "avatar", strRef, "image/png", 
            ".NET GitHub avatar");
    }
}
@page "/show-image-2"
@inject HttpClient Http
@inject IJSRuntime JS

<h1>Show Image Example 2</h1>

<button @onclick="SetImageAsync">
    Set Image
</button>

<div class="p-3">
    <img id="avatar" />
</div>

@code {
    private async Task<Stream> GetImageStreamAsync()
    {
        return await Http.GetStreamAsync(
            "https://avatars.githubusercontent.com/u/9141961");
    }

    private async Task SetImageAsync()
    {
        var imageStream = await GetImageStreamAsync();
        var strRef = new DotNetStreamReference(imageStream);
        await JS.InvokeVoidAsync("setSource", "avatar", strRef, "image/png", 
            ".NET GitHub avatar");
    }
}
@page "/show-image-2"
@inject HttpClient Http
@inject IJSRuntime JS

<h1>Show Image Example 2</h1>

<button @onclick="SetImageAsync">
    Set Image
</button>

<div class="p-3">
    <img id="avatar" />
</div>

@code {
    private async Task<Stream> GetImageStreamAsync()
    {
        return await Http.GetStreamAsync(
            "https://avatars.githubusercontent.com/u/9141961");
    }

    private async Task SetImageAsync()
    {
        var imageStream = await GetImageStreamAsync();
        var strRef = new DotNetStreamReference(imageStream);
        await JS.InvokeVoidAsync("setSource", "avatar", strRef, "image/png", 
            ".NET GitHub avatar");
    }
}

Le composant ShowFile suivant charge un fichier texte (files/quote.txt) ou un fichier PDF (files/quote.pdf) dans un élément <iframe> (documentation MDN).

Attention

L’utilisation de l’élément <iframe> dans l’exemple suivant est sécurisée et ne nécessite pas d’utiliser un sandbox, car le contenu est chargé à partir de l’application, qui est une source approuvée.

Lors du chargement de contenu à partir d’une source ou d’une entrée utilisateur non approuvée, un élément <iframe> mal implémenté risque de créer des vulnérabilités de sécurité.

ShowFile.razor:

@page "/show-file"
@inject NavigationManager Navigation
@inject HttpClient Http
@inject IJSRuntime JS

<PageTitle>Show File</PageTitle>

<div class="d-flex flex-column">
    <h1>Show File Example</h1>
    <div class="mb-4">
        <button @onclick="@(() => ShowFileAsync("files/quote.txt", 
                "General Ravon quote (text file)"))">
            Show text ('quote.txt')
        </button>
        <button @onclick="@(() => ShowFileAsync("files/quote.pdf", 
                "General Ravon quote (PDF file)"))">
            Show PDF ('quote.pdf')
        </button>
    </div>
    <iframe id="iframe" style="height: calc(100vh - 200px)" />
</div>

@code
{
    private async Task<(Stream, string?)> DownloadFileAsync(string url)
    {
        var absoluteUrl = Navigation.ToAbsoluteUri(url);
        Console.WriteLine($"Downloading file from {absoluteUrl}");

        var response = await Http.GetAsync(absoluteUrl);
        string? contentType = null;

        if (response.Content.Headers.TryGetValues("Content-Type", out var values))
        {
            contentType = values.FirstOrDefault();
        }

        return (await response.Content.ReadAsStreamAsync(), contentType);
    }

    private async Task ShowFileAsync(string url, string title)
    {
        var (fileStream, contentType) = await DownloadFileAsync(url);
        var strRef = new DotNetStreamReference(fileStream);
        await JS.InvokeVoidAsync("setSource", "iframe", strRef, contentType, title);
    }
}
@page "/show-file"
@inject NavigationManager Navigation
@inject HttpClient Http
@inject IJSRuntime JS

<PageTitle>Show File</PageTitle>

<div class="d-flex flex-column">
    <h1>Show File Example</h1>
    <div class="mb-4">
        <button @onclick="@(() => ShowFileAsync("files/quote.txt", 
                "General Ravon quote (text file)"))">
            Show text ('quote.txt')
        </button>
        <button @onclick="@(() => ShowFileAsync("files/quote.pdf", 
                "General Ravon quote (PDF file)"))">
            Show PDF ('quote.pdf')
        </button>
    </div>
    <iframe id="iframe" style="height: calc(100vh - 200px)" />
</div>

@code
{
    private async Task<(Stream, string?)> DownloadFileAsync(string url)
    {
        var absoluteUrl = Navigation.ToAbsoluteUri(url);
        Console.WriteLine($"Downloading file from {absoluteUrl}");

        var response = await Http.GetAsync(absoluteUrl);
        string? contentType = null;

        if (response.Content.Headers.TryGetValues("Content-Type", out var values))
        {
            contentType = values.FirstOrDefault();
        }

        return (await response.Content.ReadAsStreamAsync(), contentType);
    }

    private async Task ShowFileAsync(string url, string title)
    {
        var (fileStream, contentType) = await DownloadFileAsync(url);
        var strRef = new DotNetStreamReference(fileStream);
        await JS.InvokeVoidAsync("setSource", "iframe", strRef, contentType, title);
    }
}
@page "/show-file"
@inject NavigationManager NavigationManager
@inject HttpClient Http
@inject IJSRuntime JS

<div class="d-flex flex-column">
    <h1>Show File Example</h1>
    <div class="mb-4">
        <button @onclick="@(() => ShowFileAsync("files/quote.txt", 
                "General Ravon quote (text file)"))">
            Show text ('quote.txt')
        </button>
        <button @onclick="@(() => ShowFileAsync("files/quote.pdf", 
                "General Ravon quote (PDF file)"))">
            Show PDF ('quote.pdf')
        </button>
    </div>
    <iframe id="iframe" style="height: calc(100vh - 200px)" />
</div>

@code
{
    private async Task<(Stream, string?)> DownloadFileAsync(string url)
    {
        var absoluteUrl = NavigationManager.ToAbsoluteUri(url);
        Console.WriteLine($"Downloading file from {absoluteUrl}");

        var response = await Http.GetAsync(absoluteUrl);
        string? contentType = null;

        if (response.Content.Headers.TryGetValues("Content-Type", out var values))
        {
            contentType = values.FirstOrDefault();
        }

        return (await response.Content.ReadAsStreamAsync(), contentType);
    }

    private async Task ShowFileAsync(string url, string title)
    {
        var (fileStream, contentType) = await DownloadFileAsync(url);
        var strRef = new DotNetStreamReference(fileStream);
        await JS.InvokeVoidAsync("setSource", "iframe", strRef, contentType, title);
    }
}
@page "/show-file"
@inject NavigationManager NavigationManager
@inject HttpClient Http
@inject IJSRuntime JS

<div class="d-flex flex-column">
    <h1>Show File Example</h1>
    <div class="mb-4">
        <button @onclick="@(() => ShowFileAsync("files/quote.txt", 
                "General Ravon quote (text file)"))">
            Show text ('quote.txt')
        </button>
        <button @onclick="@(() => ShowFileAsync("files/quote.pdf", 
                "General Ravon quote (PDF file)"))">
            Show PDF ('quote.pdf')
        </button>
    </div>
    <iframe id="iframe" style="height: calc(100vh - 200px)" />
</div>

@code
{
    private async Task<(Stream, string?)> DownloadFileAsync(string url)
    {
        var absoluteUrl = NavigationManager.ToAbsoluteUri(url);
        Console.WriteLine($"Downloading file from {absoluteUrl}");

        var response = await Http.GetAsync(absoluteUrl);
        string? contentType = null;

        if (response.Content.Headers.TryGetValues("Content-Type", out var values))
        {
            contentType = values.FirstOrDefault();
        }

        return (await response.Content.ReadAsStreamAsync(), contentType);
    }

    private async Task ShowFileAsync(string url, string title)
    {
        var (fileStream, contentType) = await DownloadFileAsync(url);
        var strRef = new DotNetStreamReference(fileStream);
        await JS.InvokeVoidAsync("setSource", "iframe", strRef, contentType, title);
    }
}

Ressources supplémentaires