diff --git a/aspnetcore/blazor/file-downloads.md b/aspnetcore/blazor/file-downloads.md index be4b83a8d7..e5ae70eb01 100644 --- a/aspnetcore/blazor/file-downloads.md +++ b/aspnetcore/blazor/file-downloads.md @@ -50,12 +50,12 @@ For this demonstration, the app creates a 50 KB file of random data from a new b } ``` -Create a `DownloadFileFromStream` method that performs the following steps: +The following `DownloadFileFromStream` method performs the following steps: -* Retrieve the from `GetFileStream`. +* Retrieves the from `GetFileStream`. * Specify a file name when file is saved on the user's machine. The following example names the file `log.bin`. -* Wrap the in a , which allows streaming the file data to the client. -* Invoke `downloadFileFromStream`, which is a JavaScript function that accepts the data on the client. The `downloadFileFromStream` function is shown later in this article. +* Wraps the in a , which allows streaming the file data to the client. +* Invokes `downloadFileFromStream`, which is a JavaScript function that accepts the data on the client. The `downloadFileFromStream` function is shown later in this article. ```razor @code { diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index 0f491f984f..6d16a6fe08 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -744,6 +744,7 @@ In Blazor Server, file data is streamed over the SignalR connection into .NET co ## Additional resources * +* * * diff --git a/aspnetcore/blazor/images.md b/aspnetcore/blazor/images.md new file mode 100644 index 0000000000..ccb9cdbc8e --- /dev/null +++ b/aspnetcore/blazor/images.md @@ -0,0 +1,204 @@ +--- +title: Work with images in ASP.NET Core Blazor apps +author: TanayParikh +description: Learn how to work with images in ASP.NET Core Blazor apps. +monikerRange: '>= aspnetcore-6.0' +ms.author: taparik +ms.custom: mvc +no-loc: [Home, Privacy, Kestrel, appsettings.json, "ASP.NET Core Identity", cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR] +ms.date: 11/02/2021 +uid: blazor/images +--- +# Work with images in ASP.NET Core Blazor apps + +This article describes common scenarios for working with images in Blazor apps. + +## Dynamically set an image source + +The following example demonstrates how to dynamically set an image's source with a C# field. + +For the example in this section: + +* Obtain three small PNG images from any source. +* Name the images `image1.png`, `image2.png`, and `image3.png`. +* Place the images in a new folder (`images`) in the app's static assets folder (`wwwroot`). + +The following directory tree shows the images in the `wwwroot/images` folder: + +* `wwwroot` + * ... + * `images` + * `image1.png` + * `image2.png` + * `image3.png` + +In the following `ShowImage` component: + +* The image's source (`src`) is dynamically set to the value of `imageSource` in C#. +* The `ShowImage` method updates the `imageSource` field based on an image `id` argument passed to the method. +* Rendered buttons call the `ShowImage` method with an image ID argument for each of the three available images in the `images` folder. + +`Pages/ShowImage.razor`: + +```razor +@page "/show-image" + +@if (imageSource is not null) +{ +
+ +
+} + +@for (var i = 1; i <= 3; i++) +{ + var imageId = i; + +} + +@code { + private string? imageSource; + + private void ShowImage(int id) + { + imageSource = $"images/image{id}.png"; + } +} +``` + +The preceding example uses a C# field to hold the image's source data, but you can also use a C# property to hold the data. + +> [!NOTE] +> Do **not** use a loop variable directly in a lambda expression, such as `i` in the preceding `for` loop example. Otherwise, the same variable is used by all lambda expressions, which results in use of the same value in all lambdas. Always capture the variable's value in a local variable and then use the local variable. In the preceding example: +> +> * The loop variable `i` is assigned to `imageId`. +> * `imageId` is used in the lambda expression. + +## Streaming examples + +The examples in this section stream image source data using JS interop. The following JavaScript `setImageUsingStreaming` function accepts the `` tag `id` and data stream for the image. The function performs the following steps: + +* Reads the provided stream into an [`ArrayBuffer`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer). +* Creates a [`Blob`](https://developer.mozilla.org/docs/Web/API/Blob) to wrap the `ArrayBuffer`. +* Creates an object URL to serve as the address for the image to be shown. +* Updates the `` element with the specified `imageElementId` with the object URL just created. + +```javascript +async function setImageUsingStreaming(imageElementId, imageStream) { + const arrayBuffer = await imageStream.arrayBuffer(); + const blob = new Blob([arrayBuffer]); + const url = URL.createObjectURL(blob); + document.getElementById(imageElementId).src = url; +} +``` + +To prevent memory leaks, call [`URL.revokeObjectURL()`](https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL) to dispose of the object URL (`url` in the preceding example) when the component is finished working with an image. In a form, the object URL is typically revoked after the user submits the form for processing, as the object URL is no longer required at that point. + +```javascript +URL.revokeObjectURL(url); +``` + +### Stream image data to a client + +Sometimes, it's necessary to send an image directly to the client instead of hosting the image in a public directory. The following guidance explains how how to accomplish this goal using Blazor's streaming interop features. + +Add [`@inject`](xref:mvc/views/razor#inject) directives for the following services to a Razor component (`.razor`): + +* +* + +> [!NOTE] +> Blazor Server apps use a dedicated `HttpClient` service to make requests. If you haven't already added an `HttpClient` to the app's service collection, do so now by adding `builder.Services.AddHttpClient();` in the `Program.cs` file before `builder.Build()`. For more information, see . + +Add an `` tag to display the image. Also, add a button to trigger .NET to send the image to the client with a click event handler that calls a `SetImageUsingStreamingAsync` method: + +```razor + + + +``` + +Add a C# method that retrieves a for the image. At this point, you may dynamically generate an image based on the specific user or retrieve an image from storage. The following example retrieves the `dotnet` avatar from GitHub: + +```razor +@code { + private async Task GetImageStreamAsync() + { + return await HttpClient.GetStreamAsync( + "https://avatars.githubusercontent.com/u/9141961"); + } +} +``` + +Add the following `SetImageUsingStreamingAsync` method, which is triggered on the button's selection by the user. `SetImageUsingStreamingAsync` performs the following steps: + +* Retrieves the from `GetImageStreamAsync`. +* Wraps the in a , which allows streaming the image data to the client. +* Invokes `setImageUsingStreaming` ([shown earlier](#streaming-examples)), which is a JavaScript function that accepts the data on the client. The `setImageUsingStreaming` function is shown later in this article. + +```razor +@code { + private async Task SetImageUsingStreamingAsync() + { + var imageStream = await GetImageStreamAsync(); + var dotnetImageStream = new DotNetStreamReference(imageStream); + await JSRuntime.InvokeVoidAsync("setImageUsingStreaming", + "image1", dotnetImageStream); + } +} +``` + +### Preview an image provided by the `InputFile` component + +Use the component to read browser file data into .NET code. In some apps, you may wish to show a preview of a selected image. + +Add an `` tag for displaying the image preview in a Razor component (`.razor`): + +```html + +``` + +Add the following tag to the component: + +```razor + +``` + +When a file is selected, the `ResizeAndDisplayImageUsingStreaming` method is called with . Examine the following `ResizeAndDisplayImageUsingStreaming` method example: + +```razor +@code { + private async Task ResizeAndDisplayImageUsingStreaming(InputFileChangeEventArgs e) + { + var imageFile = e.File; + var resizedImage = + await imageFile.RequestImageFileAsync("image/jpg", 250, 250); + var jsImageStream = resizedImage.OpenReadStream(); + var dotnetImageStream = new DotNetStreamReference(jsImageStream); + await JSRuntime.InvokeVoidAsync("setImageUsingStreaming", + "showImageHere", dotnetImageStream); + } +} +``` + +The preceding `ResizeAndDisplayImageUsingStreaming` method performs the following steps: + +* Accesses the which is an . +* Requests an image file from the specified and resizes it to 250 pixels by 250 pixels. +* Opens a to read the `resizedImage`. +* Wraps the `resizedImage` in a , which allows streaming the image data to the client. +* Invokes `setImageUsingStreaming` ([shown earlier](#streaming-examples)), which is a JavaScript function that accepts the data on the client. + +> [!NOTE] +> The image preview technique described in this section involves round-tripping the image data from the client to the server and back. In a future release, this aspect might be optimized to better facilitate image previews. In the meantime, you may elect to create an event listener for the `InputFile` component that captures the [`FileList`](https://developer.mozilla.org/docs/Web/API/FileList) and displays a preview using JavaScript. + +## Additional resources + +* +* +* +* diff --git a/aspnetcore/toc.yml b/aspnetcore/toc.yml index a26a697bb4..b6d8fbd208 100644 --- a/aspnetcore/toc.yml +++ b/aspnetcore/toc.yml @@ -399,6 +399,8 @@ uid: blazor/js-interop/call-dotnet-from-javascript - name: Call a web API uid: blazor/call-web-api + - name: Images + uid: blazor/images - name: Security and Identity items: - name: Overview