From e06d4f8f5191ed0dd4b7ec148aba0166a54246a8 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:46:16 -0400 Subject: [PATCH 01/16] Request Streaming upload --- aspnetcore/blazor/file-uploads.md | 34 +++++++++++++++---- .../aspnetcore-9/includes/blazor.md | 6 ++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index b713b2747872..3c2826848bcf 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -101,26 +101,46 @@ If you're using the [Autofac Inversion of Control (IoC) container](https://autof ## File size read and upload limits -:::moniker range=">= aspnetcore-6.0" +:::moniker range=">= aspnetcore-9.0" + +Server-side or client-side, there's no file read or upload size limit for the component. + +For Chromium-based browsers (Google Chrome and Microsoft Edge) using the HTTP/2 protocol, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (> 2 GB or larger than the device's available memory). + +For non-Chromium browsers or without HTTP/2 protocol, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads (> 250 MB) may fail for client-side uploads using the component. For more information, see the following discussions: + +* [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) +* [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) + +For large client-side file uploads that fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. + +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-9.0" Server-side or client-side, there's no file read or upload size limit specifically for the component. However, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads (> 250 MB) may fail for client-side uploads using the component. For more information, see the following discussions: +* [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) +* [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) + +For large client-side file uploads that fail when attempting to use the component, we recommend: + +* Adopting .NET 9 or later for Chromium-based browsers (Google Chrome and Microsoft Edge) with HTTP/2 protocol support, where this problem is internally addressed by Blazor with [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API). +* For non-Chromium browsers, Chromium browsers without HTTP/2, or .NET 8 or earlier, chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. + :::moniker-end :::moniker range="< aspnetcore-6.0" The maximum supported file size for the component is 2 GB. Additionally, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads (> 250 MB) may fail for client-side uploads using the component. For more information, see the following discussions: -:::moniker-end - * [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) * [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) -For large client-side file uploads that fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. +* Adopting .NET 9 or later for Chromium-based browsers (Google Chrome and Microsoft Edge) with HTTP/2 protocol support, where this problem is internally addressed by Blazor with [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API). +* For non-Chromium browsers, Chromium browsers without HTTP/2, or .NET 5 or earlier, chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. - - -Work is currently scheduled for .NET 9 (late 2024) to address the client-side file size upload limitation. +:::moniker-end ## Examples diff --git a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md index ebe19d8ee6e8..75a7fc41b30b 100644 --- a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md @@ -235,3 +235,9 @@ The component now s Support for multiple Blazor Web Apps per server project will be considered for .NET 10 (November, 2025). For more information, see [Support for multiple Blazor Web apps per server project (`dotnet/aspnetcore` #52216)](https://github.com/dotnet/aspnetcore/issues/52216). + +### File size read and upload limits + +For Chromium-based browsers (Google Chrome and Microsoft Edge) using the HTTP/2 protocol, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (> 2 GB or larger than the device's available memory). + +For more information, see . From ec9d5afa3486bcb05c6d4650530fc21c5bb4cb77 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 25 Sep 2024 07:56:21 -0400 Subject: [PATCH 02/16] Updates --- aspnetcore/blazor/call-web-api.md | 47 +++- aspnetcore/blazor/file-uploads.md | 264 ++++++++++++++++-- .../aspnetcore-9/includes/blazor.md | 9 +- 3 files changed, 288 insertions(+), 32 deletions(-) diff --git a/aspnetcore/blazor/call-web-api.md b/aspnetcore/blazor/call-web-api.md index 258645289e62..de3286db6d51 100644 --- a/aspnetcore/blazor/call-web-api.md +++ b/aspnetcore/blazor/call-web-api.md @@ -5,7 +5,7 @@ description: Learn how to call a web API from Blazor apps. monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 03/08/2024 +ms.date: 09/25/2024 uid: blazor/call-web-api --- # Call a web API from ASP.NET Core Blazor @@ -242,6 +242,37 @@ You can address this by flowing prerendered state using the Persistent Component :::moniker-end +:::moniker range=">= aspnetcore-9.0" + +## Client-side request streaming + +For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests). + +To enable request streaming, set to `true` on the . + +In the following file upload example: + +* `content` is the file's . +* `/Filesave` is the web API endpoint. +* `Http` is the . + +```csharp +var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave"); +request.SetBrowserRequestStreamingEnabled(true); +request.Content = content; + +var response = await Http.SendAsync(request); +``` + +Streaming requests: + +* Require HTTPS protocol and don't work on HTTP/1.x. +* Include a body but not a `Content-Length` header. [CORS](xref:security/cors) is required, and CORS preflight request is always issued. + +For more information on file uploads with an component, see and the example at [Upload files to a server with client-side rendering (CSR)](xref:blazor/file-uploads#upload-files-to-a-server-with-client-side-rendering-csr). + +:::moniker-end + ## Add the `HttpClient` service *The guidance in this section applies to client-side scenarios.* @@ -257,10 +288,7 @@ In the `Program` file, add an service if it is ```csharp builder.Services.AddScoped(sp => - new HttpClient - { - BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) - }); + new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); ``` The preceding example sets the base address with `builder.HostEnvironment.BaseAddress` (), which gets the base address for the app and is typically derived from the `` tag's `href` value in the host page. @@ -274,10 +302,7 @@ If you're calling an external web API (not in the same URL space as the client a ```csharp builder.Services.AddScoped(sp => - new HttpClient - { - BaseAddress = new Uri("https://localhost:5001") - }); + new HttpClient { BaseAddress = new Uri("https://localhost:5001") }); ``` ## JSON helpers @@ -524,7 +549,7 @@ If you're calling an external web API (not in the same URL space as the client a ```csharp builder.Services.AddHttpClient("WebAPI", client => - client.BaseAddress = new Uri(https://localhost:5001)); + client.BaseAddress = new Uri("https://localhost:5001")); ``` In the following component code: @@ -624,7 +649,7 @@ If you're calling an external web API (not in the same URL space as the client a ```csharp builder.Services.AddHttpClient(client => - client.BaseAddress = new Uri(https://localhost:5001)); + client.BaseAddress = new Uri("https://localhost:5001")); ``` Components inject the typed to call the web API. diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index 3c2826848bcf..fdc8f2395f11 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -5,7 +5,7 @@ description: Learn how to upload files in Blazor with the InputFile component. monikerRange: '>= aspnetcore-5.0' ms.author: riande ms.custom: mvc -ms.date: 08/29/2024 +ms.date: 09/25/2024 uid: blazor/file-uploads --- # ASP.NET Core Blazor file uploads @@ -105,14 +105,14 @@ If you're using the [Autofac Inversion of Control (IoC) container](https://autof Server-side or client-side, there's no file read or upload size limit for the component. -For Chromium-based browsers (Google Chrome and Microsoft Edge) using the HTTP/2 protocol, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (> 2 GB or larger than the device's available memory). +For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (over 250 MB or larger than the device's available memory) with [request streaming](xref:blazor/call-web-api#client-side-request-streaming). -For non-Chromium browsers or without HTTP/2 protocol, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads (> 250 MB) may fail for client-side uploads using the component. For more information, see the following discussions: +For non-Chromium browsers or without HTTP/2 protocol/HTTPS/CORS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads, typically 250 MB or larger, may fail for client-side uploads using the component. For more information, see the following discussions: * [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) * [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) -For large client-side file uploads that fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. +For large client-side file uploads that don't meet the preceding criteria for request streaming and fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. :::moniker-end @@ -356,7 +356,7 @@ The following `FileUpload2` component: :::moniker range=">= aspnetcore-8.0" -If the component limits file uploads to a single file at a time or if the component only adopts interactive client-side rendering (CSR, `InteractiveWebAssembly`), the component can avoid the use of the `LazyBrowserFileStream` and use a . The following demonstrates the changes for the `FileUpload2` component: +If the component limits file uploads to a single file at a time or if the component only adopts client-side rendering (CSR, `InteractiveWebAssembly`), the component can avoid the use of the `LazyBrowserFileStream` and use a . The following demonstrates the changes for the `FileUpload2` component: ```diff - var stream = new LazyBrowserFileStream(file, maxFileSize); @@ -508,6 +508,12 @@ The server app must register controller services and map controller endpoints. F The following example demonstrates uploading files to a backend web API controller in a separate app, possibly on a separate server, from a component in a Blazor Web App that adopts CSR or a component in a Blazor WebAssembly app. +:::moniker range=">= aspnetcore-9.0" + +The example adopts [request streaming](xref:blazor/call-web-api#client-side-request-streaming) for a Chromium-based browser (for example, Google Chrome or Microsoft Edge) with HTTP/2 protocol, [CORS](xref:security/cors), and HTTPS. If request streaming can't be used, Blazor gracefully degrades to [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API) without request streaming. For more information, see the [File size read and upload limits](#file-size-read-and-upload-limits) section. + +:::moniker-end + The following `UploadResult` class maintains the result of an uploaded file. When a file fails to upload on the server, an error code is returned in `ErrorCode` for display to the user. A safe file name is generated on the server for each file and returned to the client in `StoredFileName` for display. Files are keyed between the client and server using the unsafe/untrusted file name in `FileName`. `UploadResult.cs`: @@ -546,15 +552,15 @@ A security best practice for production apps is to avoid sending error messages :::moniker range=">= aspnetcore-8.0" -In the Blazor Web App main project, add and related services in the project's `Program` file: +In the Blazor Web App server project, add and related services in the project's `Program` file: ```csharp builder.Services.AddHttpClient(); ``` -The `HttpClient` services must be added to the main project because the client-side component is prerendered on the server. If you [disable prerendering for the following component](xref:blazor/components/render-modes#prerendering), you aren't required to provide the `HttpClient` services in the main app and don't need to add the preceding line to the main project. +The services must be added to the server project because the client-side component is prerendered on the server. If you [disable prerendering for the following component](xref:blazor/components/render-modes#prerendering), you aren't required to provide the services in the server project and don't need to add the preceding line to the server project. -For more information on adding `HttpClient` services to an ASP.NET Core app, see . +For more information on adding services to an ASP.NET Core app, see . The client project (`.Client`) of a Blazor Web App must also register an for HTTP POST requests to a backend web API controller. Confirm or add the following to the client project's `Program` file: @@ -565,17 +571,177 @@ builder.Services.AddScoped(sp => The preceding example sets the base address with `builder.HostEnvironment.BaseAddress` (), which gets the base address for the app and is typically derived from the `` tag's `href` value in the host page. If you're calling an external web API, set the URI to the web API's base address. -Specify the Interactive WebAssembly render mode attribute at the top of the following component in a Blazor Web App: +A standalone Blazor WebAssembly app that uploads files to a separate server web API either uses a [named `HttpClient`](xref:blazor/call-web-api#named-httpclient-with-ihttpclientfactory) or sets the default service registration to point to the web API's endpoint. In the following example where the web API is hosted locally at port 5001, the base address is `https://localhost:5001`: + +```csharp +builder.Services.AddScoped(sp => + new HttpClient { BaseAddress = new Uri("https://localhost:5001") }); +``` + +:::moniker-end + +:::moniker range=">= aspnetcore-9.0" + +In a Blazor Web App, add the namespace to the component's directives: ```razor -@rendermode InteractiveWebAssembly +@using Microsoft.AspNetCore.Components.WebAssembly.Http ``` :::moniker-end `FileUpload2.razor`: -:::moniker range=">= aspnetcore-8.0" +:::moniker range=">= aspnetcore-9.0" + +```razor +@page "/file-upload-2" +@using System.Linq +@using System.Net.Http.Headers +@using System.Net +@inject HttpClient Http +@inject ILogger Logger + +File Upload 2 + +

File Upload Example 2

+ +

+ +

+ +@if (files.Count > 0) +{ +
+
+
    + @foreach (var file in files) + { +
  • + File: @file.Name +
    + @if (FileUpload(uploadResults, file.Name, Logger, + out var result)) + { + + Stored File Name: @result.StoredFileName + + } + else + { + + There was an error uploading the file + (Error: @result.ErrorCode). + + } +
  • + } +
+
+
+} + +@code { + private List files = new(); + private List uploadResults = new(); + private int maxAllowedFiles = 3; + private bool shouldRender; + + protected override bool ShouldRender() => shouldRender; + + private async Task OnInputFileChange(InputFileChangeEventArgs e) + { + shouldRender = false; + long maxFileSize = 1024 * 15; + var upload = false; + + using var content = new MultipartFormDataContent(); + + foreach (var file in e.GetMultipleFiles(maxAllowedFiles)) + { + if (uploadResults.SingleOrDefault( + f => f.FileName == file.Name) is null) + { + try + { + files.Add(new() { Name = file.Name }); + + var fileContent = new StreamContent(file.OpenReadStream(maxFileSize)); + + fileContent.Headers.ContentType = + new MediaTypeHeaderValue(file.ContentType); + + content.Add( + content: fileContent, + name: "\"files\"", + fileName: file.Name); + + upload = true; + } + catch (Exception ex) + { + Logger.LogInformation( + "{FileName} not uploaded (Err: 6): {Message}", + file.Name, ex.Message); + + uploadResults.Add( + new() + { + FileName = file.Name, + ErrorCode = 6, + Uploaded = false + }); + } + } + } + + if (upload) + { + var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave"); + request.SetBrowserRequestStreamingEnabled(true); + request.Content = content; + + var response = await Http.SendAsync(request); + + var newUploadResults = await response.Content + .ReadFromJsonAsync>(); + + if (newUploadResults is not null) + { + uploadResults = uploadResults.Concat(newUploadResults).ToList(); + } + } + + shouldRender = true; + } + + private static bool FileUpload(IList uploadResults, + string? fileName, ILogger logger, out UploadResult result) + { + result = uploadResults.SingleOrDefault(f => f.FileName == fileName) ?? new(); + + if (!result.Uploaded) + { + logger.LogInformation("{FileName} not uploaded (Err: 5)", fileName); + result.ErrorCode = 5; + } + + return result.Uploaded; + } + + private class File + { + public string? Name { get; set; } + } +} +``` + +:::moniker-end + +:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_WebAssembly/Pages/FileUpload2.razor"::: @@ -615,7 +781,13 @@ Because the example uses the app's [environment](xref:blazor/fundamentals/enviro > [!WARNING] > The example saves files without scanning their contents, and the guidance in this article doesn't take into account additional security best practices for uploaded files. On staging and production systems, disable execute permission on the upload folder and scan files with an anti-virus/anti-malware scanner API immediately after upload. For more information, see . -In the following example, update the shared project's namespace to match the shared project if a shared project is supplying the `UploadResult` class. +In the following example for a hosted Blazor WebAssembly app or where a shared project is used to supply the `UploadResult` class, add the shared project's namespace: + +```csharp +using BlazorSample.Shared; +``` + +We recommend using a namespace for the following controller (for example: `namespace BlazorSample.Controllers`). `Controllers/FilesaveController.cs`: @@ -628,7 +800,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using BlazorSample.Shared; [ApiController] [Route("[controller]")] @@ -723,15 +894,72 @@ public class FilesaveController : ControllerBase In the preceding code, is called to generate a secure file name. Never trust the file name provided by the browser, as an attacker may choose an existing file name that overwrites an existing file or send a path that attempts to write outside of the app. -The server app must register controller services and map controller endpoints. For more information, see . +The server app must register controller services and map controller endpoints. For more information, see . We recommend adding controller services with in order to automatically [mitigate Cross-Site Request Forgery (XSRF/CSRF) attacks](xref:security/anti-request-forgery) for authenticated users. If you merely use , antiforgery isn't enabled automatically. For more information, see . + +:::moniker range=">= aspnetcore-9.0" + +Cross-Origin Requests (CORS) configuration on the server is required for [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests), and a preflight request is always made by the client. In the service configuration of the server's `Program` file (the server project of a Blazor Web App or the backend server web API of a Blazor WebAssembly app), the following default CORS policy is suitable for testing with the examples in this article. The client makes the local request from port 5003. Change the port number to match the client app port that you're using: + +:::moniker-end - +Configure Cross-Origin Requests (CORS) on the server. In the service configuration of the backend server web API's `Program` file, the following default CORS policy is suitable for testing with the examples in this article. The client makes the local request from port 5003. Change the port number to match the client app port that you're using: + +:::moniker-end + +```csharp +builder.Services.AddCors(options => +{ + options.AddDefaultPolicy( + policy => + { + policy.WithOrigins("https://localhost:5003") + .AllowAnyMethod() + .AllowAnyHeader(); + }); +}); +``` + +After calling in the `Program` file, call to add CORS middleware: + +```csharp +app.UseCors(); +``` + +For more information, see . + +:::moniker range=">= aspnetcore-9.0" + +Configure the server's maximum request body size and multipart body length limits if the limits constrain the upload size. + +For the Kestrel server, set (default: 30,000,000 bytes) and (default: 134,217,728 bytes). Set the `maxFileSize` variable in the component and the controller to the same value. + +In the following `Program` file Kestrel configuration (the server project of a Blazor Web App or the backend server web API of a Blazor WebAssembly app), the `{LIMIT}` placeholder is the limit in bytes: + +```csharp +using Microsoft.AspNetCore.Http.Features; + +... + +builder.WebHost.ConfigureKestrel(serverOptions => +{ + serverOptions.Limits.MaxRequestBodySize = {LIMIT}; +}); + +builder.Services.Configure(options => +{ + options.MultipartBodyLengthLimit = {LIMIT}; +}); +``` + +:::moniker-end ## Cancel a file upload diff --git a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md index 75a7fc41b30b..2d356d4b2044 100644 --- a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md @@ -236,8 +236,11 @@ Support for multiple Blazor Web Apps per server project will be considered for . For more information, see [Support for multiple Blazor Web apps per server project (`dotnet/aspnetcore` #52216)](https://github.com/dotnet/aspnetcore/issues/52216). -### File size read and upload limits +### Client-side request streaming -For Chromium-based browsers (Google Chrome and Microsoft Edge) using the HTTP/2 protocol, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (> 2 GB or larger than the device's available memory). +For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, [CORS](xref:security/cors), and HTTPS, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests). -For more information, see . +For more information, see the following resources: + +* +* . From 2a0f3395def2d66a5f94d8c30f2da24e95628803 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 16 Oct 2024 09:14:38 -0400 Subject: [PATCH 03/16] Security considerations for file uploads --- aspnetcore/blazor/file-uploads.md | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index fdc8f2395f11..99e082dc063c 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -142,6 +142,48 @@ The maximum supported file size for the to impose a limit on the file size. + + The following approach is ***insecure*** and must be avoided: + +```diff +- var fileContent = new StreamContent(file.OpenReadStream(file.Size)); +``` + +Instead of using the unsafe client-supplied file size, explicitly specify the maximum file size. The following example sets the maximum file size (`maxFileSize`) to 15 K: + +```csharp +long maxFileSize = 1024 * 15; + +... + +var fileContent = new StreamContent(file.OpenReadStream(maxFileSize)); +``` + +### File name security + +Never use a client-supplied file name for saving a file to physical storage. Create a safe file name for the file using or to create a full path (including the file name) for temporary storage. + +Razor automatically HTML encodes property values for display. The following code is safe to use: + +```cshtml +@foreach (var file in Model.DatabaseFiles) { + + + @file.UntrustedName + + +} +``` + +Outside of Razor, always use to safely encode file names from a user's request. + +Many implementations must include a check that the file exists; otherwise, the file is overwritten by a file of the same name. Supply additional logic to meet your app's specifications. + ## Examples The following examples demonstrate multiple file upload in a component. allows reading multiple files. Specify the maximum number of files to prevent a malicious user from uploading a larger number of files than the app expects. allows reading the first and only file if the file upload doesn't support multiple files. From 316bd5ea12cbd8767d5fbb43ee6c6da08216df9c Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Fri, 8 Nov 2024 07:28:09 -0500 Subject: [PATCH 04/16] Updates --- aspnetcore/blazor/call-web-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/blazor/call-web-api.md b/aspnetcore/blazor/call-web-api.md index de3286db6d51..3d378dae475b 100644 --- a/aspnetcore/blazor/call-web-api.md +++ b/aspnetcore/blazor/call-web-api.md @@ -267,7 +267,7 @@ var response = await Http.SendAsync(request); Streaming requests: * Require HTTPS protocol and don't work on HTTP/1.x. -* Include a body but not a `Content-Length` header. [CORS](xref:security/cors) is required, and CORS preflight request is always issued. +* Include a body but not a `Content-Length` header. [CORS](xref:security/cors) is required, and a CORS preflight request is always issued. For more information on file uploads with an component, see and the example at [Upload files to a server with client-side rendering (CSR)](xref:blazor/file-uploads#upload-files-to-a-server-with-client-side-rendering-csr). From cdc41753c1586158fc52200b539793812f3c67b6 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 19 Nov 2024 07:06:31 -0500 Subject: [PATCH 05/16] Update --- aspnetcore/grpc/why-migrate-wcf-to-dotnet-grpc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/grpc/why-migrate-wcf-to-dotnet-grpc.md b/aspnetcore/grpc/why-migrate-wcf-to-dotnet-grpc.md index a444e6fa2d2c..0e1002e9d0bf 100644 --- a/aspnetcore/grpc/why-migrate-wcf-to-dotnet-grpc.md +++ b/aspnetcore/grpc/why-migrate-wcf-to-dotnet-grpc.md @@ -1,6 +1,6 @@ --- title: Why migrate WCF to ASP.NET Core gRPC -author: markrendle +author: jamesnk description: This article provides a summary of why ASP.NET Core gRPC is a good fit for Windows Communication Foundation (WCF) developers who want to migrate to modern architectures and platforms. monikerRange: '>= aspnetcore-3.0' ms.author: wpickett From 99733d137b72d31f87e96305c7d9c999862c10d1 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Mar 2025 10:19:42 -0500 Subject: [PATCH 06/16] Apply suggestions from code review Co-authored-by: Daniel Roth --- aspnetcore/blazor/call-web-api.md | 4 ++-- aspnetcore/blazor/file-uploads.md | 8 ++------ aspnetcore/release-notes/aspnetcore-9/includes/blazor.md | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/aspnetcore/blazor/call-web-api.md b/aspnetcore/blazor/call-web-api.md index 2721907d905b..58b8da8cec61 100644 --- a/aspnetcore/blazor/call-web-api.md +++ b/aspnetcore/blazor/call-web-api.md @@ -240,7 +240,7 @@ You can address this by flowing prerendered state using the Persistent Component ## Client-side request streaming -For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests). +For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, and HTTPS, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests). To enable request streaming, set to `true` on the . @@ -261,7 +261,7 @@ var response = await Http.SendAsync(request); Streaming requests: * Require HTTPS protocol and don't work on HTTP/1.x. -* Include a body but not a `Content-Length` header. [CORS](xref:security/cors) is required, and a CORS preflight request is always issued. +* Include a body but not a `Content-Length` header. [CORS](xref:security/cors) with a preflight request is required for cross-origin streaming requests. For more information on file uploads with an component, see and the example at [Upload files to a server with client-side rendering (CSR)](xref:blazor/file-uploads#upload-files-to-a-server-with-client-side-rendering-csr). diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index 81b57b69d513..1580aa1544ce 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -105,13 +105,9 @@ If you're using the [Autofac Inversion of Control (IoC) container](https://autof Server-side or client-side, there's no file read or upload size limit for the component. -For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (over 250 MB or larger than the device's available memory) with [request streaming](xref:blazor/call-web-api#client-side-request-streaming). - -For non-Chromium browsers or without HTTP/2 protocol/HTTPS/CORS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads, typically 250 MB or larger, may fail for client-side uploads using the component. For more information, see the following discussions: - -* [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) -* [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) +For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor supports using the [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (over 250 MB or larger than the device's available memory) with [request streaming](xref:blazor/call-web-api#client-side-request-streaming). +For non-Chromium browsers, without HTTP/2 protocol, or without HTTPS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads, typically 250 MB or larger, may fail for client-side uploads using the component. For large client-side file uploads that don't meet the preceding criteria for request streaming and fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. :::moniker-end diff --git a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md index ccbd5a57af7b..dccb6c5da4a4 100644 --- a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md @@ -230,7 +230,7 @@ For more information, see . ### Client-side request streaming -For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, [CORS](xref:security/cors), and HTTPS, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests). +Interactive WebAssembly rendering in Blazor now supports client-side request streaming using the `request.SetBrowserReqeustStreamingEnabled(true)` option on `HttpRequestMessage`. For more information, see the following resources: From 7c3a54ef4f62c51da76edc7cbdbe47e2bd2733a5 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Mar 2025 10:24:27 -0500 Subject: [PATCH 07/16] Update aspnetcore/blazor/file-uploads.md --- aspnetcore/blazor/file-uploads.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index 1580aa1544ce..c005442685e9 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -107,7 +107,8 @@ Server-side or client-side, there's no file read or upload size limit for the component. +For non-Chromium browsers, without HTTP/2 protocol, or without HTTPS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads, typically 250 MB or larger, may fail for client-side uploads using the component. + For large client-side file uploads that don't meet the preceding criteria for request streaming and fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. :::moniker-end From cbae03c395ae1c1e75392f62a93de336d167759a Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Mar 2025 15:57:10 -0500 Subject: [PATCH 08/16] Updates --- aspnetcore/blazor/file-uploads.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index c005442685e9..f736dfb90f07 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -103,8 +103,6 @@ If you're using the [Autofac Inversion of Control (IoC) container](https://autof :::moniker range=">= aspnetcore-9.0" -Server-side or client-side, there's no file read or upload size limit for the component. - For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor supports using the [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (over 250 MB or larger than the device's available memory) with [request streaming](xref:blazor/call-web-api#client-side-request-streaming). For non-Chromium browsers, without HTTP/2 protocol, or without HTTPS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads, typically 250 MB or larger, may fail for client-side uploads using the component. From 91b17b3d591bb426ab620dd8bf89c000e362a2b2 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Mar 2025 16:01:33 -0500 Subject: [PATCH 09/16] Update aspnetcore/blazor/file-uploads.md Co-authored-by: Daniel Roth --- aspnetcore/blazor/file-uploads.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index f736dfb90f07..edfcd8932945 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -103,7 +103,7 @@ If you're using the [Autofac Inversion of Control (IoC) container](https://autof :::moniker range=">= aspnetcore-9.0" -For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor supports using the [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (over 250 MB or larger than the device's available memory) with [request streaming](xref:blazor/call-web-api#client-side-request-streaming). +For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor supports using the [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files with [request streaming](xref:blazor/call-web-api#client-side-request-streaming). For non-Chromium browsers, without HTTP/2 protocol, or without HTTPS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads, typically 250 MB or larger, may fail for client-side uploads using the component. From f02cd12e4d0bf00aa47c7802c03cb10bf918697a Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Mar 2025 16:03:19 -0500 Subject: [PATCH 10/16] Apply suggestions from code review Co-authored-by: Daniel Roth --- aspnetcore/blazor/file-uploads.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index edfcd8932945..bd010bbc2a54 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -105,7 +105,7 @@ If you're using the [Autofac Inversion of Control (IoC) container](https://autof For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor supports using the [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files with [request streaming](xref:blazor/call-web-api#client-side-request-streaming). -For non-Chromium browsers, without HTTP/2 protocol, or without HTTPS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads, typically 250 MB or larger, may fail for client-side uploads using the component. +For non-Chromium browsers, without HTTP/2 protocol, or without HTTPS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads may fail for client-side uploads using the component. For large client-side file uploads that don't meet the preceding criteria for request streaming and fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. @@ -113,7 +113,7 @@ For large client-side file uploads that don't meet the preceding criteria for re :::moniker range=">= aspnetcore-6.0 < aspnetcore-9.0" -Server-side or client-side, there's no file read or upload size limit specifically for the component. However, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads (> 250 MB) may fail for client-side uploads using the component. For more information, see the following discussions: +Server-side or client-side, there's no file read or upload size limit specifically for the component. However, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads may fail for client-side uploads using the component. For more information, see the following discussions: * [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) * [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) From 98f0866326810472e1639264d10ff8600f3bab85 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Mar 2025 16:13:00 -0500 Subject: [PATCH 11/16] Apply suggestions from code review Co-authored-by: Daniel Roth --- aspnetcore/blazor/file-uploads.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index bd010bbc2a54..ba2d1f177269 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -127,10 +127,7 @@ For large client-side file uploads that fail when attempting to use the component is 2 GB. Additionally, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads (> 250 MB) may fail for client-side uploads using the component. For more information, see the following discussions: - -* [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) -* [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) +The maximum supported file size for the component is 2 GB. Additionally, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. * Adopting .NET 9 or later for Chromium-based browsers (Google Chrome and Microsoft Edge) with HTTP/2 protocol support, where this problem is internally addressed by Blazor with [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API). * For non-Chromium browsers, Chromium browsers without HTTP/2, or .NET 5 or earlier, chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. @@ -539,7 +536,7 @@ The following example demonstrates uploading files to a backend web API controll :::moniker range=">= aspnetcore-9.0" -The example adopts [request streaming](xref:blazor/call-web-api#client-side-request-streaming) for a Chromium-based browser (for example, Google Chrome or Microsoft Edge) with HTTP/2 protocol, [CORS](xref:security/cors), and HTTPS. If request streaming can't be used, Blazor gracefully degrades to [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API) without request streaming. For more information, see the [File size read and upload limits](#file-size-read-and-upload-limits) section. +The example adopts [request streaming](xref:blazor/call-web-api#client-side-request-streaming) for a Chromium-based browser (for example, Google Chrome or Microsoft Edge) with HTTP/2 protocol and HTTPS. If request streaming can't be used, Blazor gracefully degrades to [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API) without request streaming. For more information, see the [File size read and upload limits](#file-size-read-and-upload-limits) section. :::moniker-end From 2d052db9cbb46865f2654dd74a6a2bea761e0c3a Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Mar 2025 16:15:51 -0500 Subject: [PATCH 12/16] Updates --- aspnetcore/blazor/file-uploads.md | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index ba2d1f177269..b64f37860f39 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -138,22 +138,11 @@ The maximum supported file size for the to impose a limit on the file size. - - The following approach is ***insecure*** and must be avoided: +Avoid using to impose a limit on the file size. Instead of using the unsafe client-supplied file size, explicitly specify the maximum file size. The following example uses the maximum file size assigned to `maxFileSize`: ```diff - var fileContent = new StreamContent(file.OpenReadStream(file.Size)); -``` - -Instead of using the unsafe client-supplied file size, explicitly specify the maximum file size. The following example sets the maximum file size (`maxFileSize`) to 15 K: - -```csharp -long maxFileSize = 1024 * 15; - -... - -var fileContent = new StreamContent(file.OpenReadStream(maxFileSize)); ++ var fileContent = new StreamContent(file.OpenReadStream(maxFileSize)); ``` ### File name security From e4f2466b650d6c43d944b681886019c7d3680461 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Mar 2025 21:02:32 -0500 Subject: [PATCH 13/16] Update aspnetcore/blazor/file-uploads.md Co-authored-by: Daniel Roth --- aspnetcore/blazor/file-uploads.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index b64f37860f39..80226de92331 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -905,7 +905,7 @@ The server app must register controller services and map controller endpoints. F :::moniker range=">= aspnetcore-9.0" -Cross-Origin Requests (CORS) configuration on the server is required for [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests), and a preflight request is always made by the client. In the service configuration of the server's `Program` file (the server project of a Blazor Web App or the backend server web API of a Blazor WebAssembly app), the following default CORS policy is suitable for testing with the examples in this article. The client makes the local request from port 5003. Change the port number to match the client app port that you're using: +Cross-Origin Requests (CORS) configuration on the server is required for [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests) when the server is hosted at a different origin, and a preflight request is always made by the client. In the service configuration of the server's `Program` file (the server project of a Blazor Web App or the backend server web API of a Blazor WebAssembly app), the following default CORS policy is suitable for testing with the examples in this article. The client makes the local request from port 5003. Change the port number to match the client app port that you're using: :::moniker-end From a0348d978d7fd24db6c0a36ce5e290f33a913d8a Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 4 Mar 2025 09:22:27 -0500 Subject: [PATCH 14/16] Updates --- aspnetcore/blazor/file-uploads.md | 50 +++++++++---------------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index 80226de92331..a94ab414fd1d 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -105,32 +105,13 @@ If you're using the [Autofac Inversion of Control (IoC) container](https://autof For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor supports using the [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files with [request streaming](xref:blazor/call-web-api#client-side-request-streaming). -For non-Chromium browsers, without HTTP/2 protocol, or without HTTPS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads may fail for client-side uploads using the component. - -For large client-side file uploads that don't meet the preceding criteria for request streaming and fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. +Without a Chromium browser, HTTP/2 protocol, or HTTPS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads may fail for client-side uploads using the component. :::moniker-end -:::moniker range=">= aspnetcore-6.0 < aspnetcore-9.0" - -Server-side or client-side, there's no file read or upload size limit specifically for the component. However, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads may fail for client-side uploads using the component. For more information, see the following discussions: - -* [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) -* [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) - -For large client-side file uploads that fail when attempting to use the component, we recommend: - -* Adopting .NET 9 or later for Chromium-based browsers (Google Chrome and Microsoft Edge) with HTTP/2 protocol support, where this problem is internally addressed by Blazor with [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API). -* For non-Chromium browsers, Chromium browsers without HTTP/2, or .NET 8 or earlier, chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. - -:::moniker-end - -:::moniker range="< aspnetcore-6.0" - -The maximum supported file size for the component is 2 GB. Additionally, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. +:::moniker range="< aspnetcore-9.0" -* Adopting .NET 9 or later for Chromium-based browsers (Google Chrome and Microsoft Edge) with HTTP/2 protocol support, where this problem is internally addressed by Blazor with [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API). -* For non-Chromium browsers, Chromium browsers without HTTP/2, or .NET 5 or earlier, chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. +Without a Chromium browser, HTTP/2 protocol, or HTTPS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads may fail for client-side uploads using the component. :::moniker-end @@ -302,15 +283,18 @@ public class UploadResult A security best practice for production apps is to avoid sending error messages to clients that might reveal sensitive information about an app, server, or network. Providing detailed error messages can aid a malicious user in devising attacks on an app, server, or network. The example code in this section only sends back an error code number (`int`) for display by the component client-side if a server-side error occurs. If a user requires assistance with a file upload, they provide the error code to support personnel for support ticket resolution without ever knowing the exact cause of the error. - + The following `LazyBrowserFileStream` class defines a custom stream type that lazily calls just before the first bytes of the stream are requested. The stream isn't transmitted from the browser to the server until reading the stream begins in .NET. `LazyBrowserFileStream.cs`: - + - + :::moniker range=">= aspnetcore-8.0" @@ -375,7 +359,7 @@ The following `FileUpload2` component: :::moniker-end - + :::moniker range=">= aspnetcore-8.0" @@ -1180,10 +1164,6 @@ For more information on SignalR configuration and how to set - Blazor relies on set to 1, which is the default value. Increasing the value leads to a high probability that `CopyTo` operations throw `System.InvalidOperationException: 'Reading is not allowed after reader was completed.'`. For more information, see [MaximumParallelInvocationsPerClient > 1 breaks file upload in Blazor Server mode (`dotnet/aspnetcore` #53951)](https://github.com/dotnet/aspnetcore/issues/53951). @@ -1196,15 +1176,13 @@ The line that calls - -* Using the [Autofac Inversion of Control (IoC) container](https://autofac.org/) instead of the built-in ASP.NET Core dependency injection container. To resolve the issue, set to `true` in the [server-side circuit handler hub options](xref:blazor/fundamentals/signalr#server-side-circuit-handler-options). For more information, see [FileUpload: Did not receive any data in the allotted time (`dotnet/aspnetcore` #38842)](https://github.com/dotnet/aspnetcore/issues/38842#issuecomment-1342540950). +* Using the [Autofac Inversion of Control (IoC) container](https://autofac.org/) instead of the built-in ASP.NET Core dependency injection container in versions of ASP.NET Core earlier than 9.0. To resolve the issue, set to `true` in the [server-side circuit handler hub options](xref:blazor/fundamentals/signalr#server-side-circuit-handler-options). For more information, see [FileUpload: Did not receive any data in the allotted time (`dotnet/aspnetcore` #38842)](https://github.com/dotnet/aspnetcore/issues/38842#issuecomment-1342540950). * Not reading the stream to completion. This isn't a framework issue. Trap the exception and investigate it further in your local environment/network. - + * Using server-side rendering and calling on multiple files before reading them to completion. To resolve the issue, use the `LazyBrowserFileStream` class and approach described in the [Upload files to a server with server-side rendering](#upload-files-to-a-server-with-server-side-rendering) section of this article. From 09cd04693b2a2c43b545bdb855d88155ada15154 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 4 Mar 2025 09:34:22 -0500 Subject: [PATCH 15/16] Updates --- aspnetcore/blazor/file-uploads.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index b5094b6ac9ba..666e17ea73c8 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -121,7 +121,7 @@ Without a Chromium browser, HTTP/2 protocol, or HTTPS, client-side Blazor reads :::moniker range="< aspnetcore-9.0" -Without a Chromium browser, HTTP/2 protocol, or HTTPS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads may fail for client-side uploads using the component. +Client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads may fail for client-side uploads using the component. We recommend adopting [request streaming](xref:blazor/call-web-api?view=aspnetcore-8.0&preserve-view=true#client-side-request-streaming) with ASP.NET Core 9.0 or later. :::moniker-end From 0529a1193a3368d3d25413d2489ab316a48e8f08 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 4 Mar 2025 09:38:03 -0500 Subject: [PATCH 16/16] Updates --- aspnetcore/blazor/file-uploads.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index 666e17ea73c8..b0460b14dab6 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -121,7 +121,7 @@ Without a Chromium browser, HTTP/2 protocol, or HTTPS, client-side Blazor reads :::moniker range="< aspnetcore-9.0" -Client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads may fail for client-side uploads using the component. We recommend adopting [request streaming](xref:blazor/call-web-api?view=aspnetcore-8.0&preserve-view=true#client-side-request-streaming) with ASP.NET Core 9.0 or later. +Client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads may fail for client-side uploads using the component. We recommend adopting [request streaming](xref:blazor/call-web-api?view=aspnetcore-9.0&preserve-view=true#client-side-request-streaming) with ASP.NET Core 9.0 or later. :::moniker-end