Compartir vía


Mostrar imágenes y documentos en ASP.NET Core Blazor

Nota:

Esta no es la versión más reciente de este artículo. Para la versión actual, consulta la versión .NET 8 de este artículo.

Advertencia

Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulta la versión .NET 8 de este artículo.

En este artículo se describen métodos para mostrar imágenes y documentos en aplicaciones Blazor.

Los ejemplos de este artículo están disponibles para su inspección y uso en las aplicaciones Blazor de ejemplo:

dotnet/blazor-samples Repositorio de GitHub: ve a la aplicación denominada BlazorSample_BlazorWebApp (8.0 o posterior), BlazorSample_Server (7.0 o anterior) o BlazorSample_WebAssembly.

Establecer dinámicamente un origen de imagen

En el ejemplo siguiente se muestra cómo establecer dinámicamente el origen de una imagen con un campo de C#.

En el ejemplo de esta sección se usan tres archivos de imagen, denominados image1.png, image2.png y image3.png. Las imágenes se colocan en una carpeta denominada images en la raíz web de la aplicación (wwwroot). El uso de la carpeta images es solo con fines de demostración. Puedes organizar los activos estáticos en la disposición de carpetas que prefieras, incluso servir activos directamente desde la carpeta wwwroot.

En el componente ShowImage1 siguiente:

  • El origen de la imagen (src) se establece dinámicamente en el valor de imageSource en C#.
  • El método ShowImage actualiza el campo imageSource en función de un argumento id de imagen pasado al método.
  • Los botones representados llaman al método ShowImage con un argumento de imagen para cada una de las tres imágenes disponibles en la carpeta images. El nombre de archivo se compone mediante el argumento pasado al método y coincide con una de las tres imágenes de la carpeta 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";
    }
}

En el ejemplo anterior se usa un campo de C# para contener los datos de origen de la imagen, pero también se puede usar una propiedad de C# para contener los datos.

No uses una variable de bucle directamente en una expresión lambda (como i en el ejemplo de bucle for anterior). De lo contrario, todas las expresiones lambda usarán la misma variable, con lo cual se usará el mismo valor en todas las expresiones lambda. Captura el valor de la variable en una variable local. En el ejemplo anterior:

  • La variable de bucle i se asigna a imageId.
  • imageId se usa en la expresión lambda.

Como alternativa, utiliza un bucle foreach con Enumerable.Range, que no sufre el problema anterior:

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

Para obtener más información sobre las expresiones lambda con el control de eventos, consulta control de eventos Blazor de ASP.NET Core.

Transmisión de datos de imagen o documento

Una imagen u otro tipo de documento, como un PDF, se puede enviar directamente al cliente mediante las características de streaming interop de Blazor en lugar de hospedar el archivo en una dirección URL pública.

El ejemplo de esta sección transmite datos de origen mediante la interoperabilidad de JavaScript (JS). La siguiente función setSourceJS:

  • Se puede usar para transmitir contenido para los siguientes elementos: <body>, <embed>, <iframe>, <img>, <link>, <object>, <script>, <style> y <track>.
  • Acepta un elemento id para mostrar el contenido del archivo, un flujo de datos para el documento, el tipo de contenido y un título para el elemento de visualización.

La función :

  • Lee la secuencia proporcionada en ArrayBuffer.
  • Crea un Blob para encapsular ArrayBuffer y establece el tipo de contenido del blob.
  • Crea un objeto de dirección URL que sirva de dirección para el documento que se va a mostrar.
  • Establece el título del elemento (title) a partir del parámetro title y establece el origen del elemento (src) a partir de la dirección URL del objeto creado.
  • Para evitar pérdidas de memoria, la función llama a revokeObjectURL para eliminar el objeto de dirección URL después de que el elemento cargue el recurso (evento 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>

Nota:

Para obtener una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulta Ubicación de JavaScript en aplicaciones Blazor de ASP.NET Core.

El componente ShowImage2 siguiente:

  • Inserta servicios para System.Net.Http.HttpClient y Microsoft.JSInterop.IJSRuntime.
  • Incluye una etiqueta <img> para mostrar una imagen.
  • Tiene un método GetImageStreamAsync de C# para recuperar un Stream para una imagen. Una aplicación de producción puede generar dinámicamente una imagen basada en el usuario específico o recuperar una imagen del almacenamiento. En el ejemplo siguiente se recupera el avatar de .NET para el repositorio de GitHub dotnet.
  • Tienes un método SetImageAsync que se desencadena en la selección del botón por parte del usuario. SetImageAsync realiza las tareas siguientes:
    • Recupera el elemento Stream de GetImageStreamAsync.
    • Ajusta la clase Stream en un DotNetStreamReference, lo que permite el streaming de los datos de archivo al cliente.
    • Invoca la función setSource de JavaScript, que acepta los datos en el cliente.

Nota:

Las aplicaciones del lado servidor usan un servicio dedicado HttpClient para realizar solicitudes, por lo que el desarrollador de una aplicación del lado Blazor servidor no requiere ninguna acción para registrar un HttpClient servicio. Las aplicaciones HttpClient tienen un registro de servicio predeterminado Blazor cuando la aplicación se crea a partir de una plantilla de proyecto. Si un HttpClient registro de servicio no está presente en el Program archivo de una aplicación del lado cliente, proporcione uno agregando builder.Services.AddHttpClient();. Para más información, consulta Realización de solicitudes HTTP con IHttpClientFactory en 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");
    }
}

El siguiente componente ShowFile carga un archivo de texto (files/quote.txt) o un archivo PDF (files/quote.pdf) en un elemento <iframe> (documentación MDN).

Precaución

El uso del elemento <iframe> en el siguiente ejemplo es seguro y no requiere de un espacio aislado porque el contenido se carga desde la aplicación, que es un origen de confianza.

Al cargar contenido desde un origen o entrada de usuario que no es de confianza, un elemento implementado <iframe> incorrectamente corre el riesgo de crear vulnerabilidades de seguridad.

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);
    }
}

Recursos adicionales