diff --git a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Controllers/StreamingController.cs b/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Controllers/StreamingController.cs index 3206cee10b06..54ed7785997d 100644 --- a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Controllers/StreamingController.cs +++ b/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Controllers/StreamingController.cs @@ -24,7 +24,7 @@ public class StreamingController : Controller private readonly AppDbContext _context; private readonly long _fileSizeLimit; private readonly ILogger _logger; - private readonly string[] _permittedExtensions = { ".txt" }; + private readonly string[] _permittedExtensions = { ".txt", ".vhdx" }; private readonly string _targetFilePath; // Get the default form options so that we can use them to set the default @@ -100,9 +100,12 @@ public async Task UploadDatabase() trustedFileNameForDisplay = WebUtility.HtmlEncode( contentDisposition.FileName.Value); - streamedFileContent = - await FileHelpers.ProcessStreamedFile(section, contentDisposition, - ModelState, _permittedExtensions, _fileSizeLimit); + using (var memoryStream = new MemoryStream()) + { + await FileHelpers.ProcessStreamedFile(section, contentDisposition, + ModelState, _permittedExtensions, _fileSizeLimit, memoryStream); + streamedFileContent = memoryStream.ToArray(); + } if (!ModelState.IsValid) { @@ -269,19 +272,18 @@ public async Task UploadPhysical() // For more information, see the topic that accompanies // this sample. - var streamedFileContent = await FileHelpers.ProcessStreamedFile( - section, contentDisposition, ModelState, - _permittedExtensions, _fileSizeLimit); - - if (!ModelState.IsValid) - { - return BadRequest(ModelState); - } - using (var targetStream = System.IO.File.Create( Path.Combine(_targetFilePath, trustedFileNameForFileStorage))) { - await targetStream.WriteAsync(streamedFileContent); + + await FileHelpers.ProcessStreamedFile( + section, contentDisposition, ModelState, + _permittedExtensions, _fileSizeLimit, targetStream); + + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } _logger.LogInformation( "Uploaded file '{TrustedFileNameForDisplay}' saved to " + diff --git a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Program.cs b/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Program.cs index 3bf3c8d62840..025e23f4fe1d 100644 --- a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Program.cs +++ b/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.DependencyInjection; @@ -20,6 +20,12 @@ public static IHostBuilder CreateHostBuilder(string[] args) => .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); + webBuilder.ConfigureKestrel(options => + { + options.Limits.MaxRequestBodySize = 6L << 30; // 6 GB + options.Limits.Http2.InitialStreamWindowSize = 16 << 20; // 16 MB + options.Limits.Http2.MaxFrameSize = (1 << 24) - 1; + }); }); } } diff --git a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/SampleApp.csproj b/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/SampleApp.csproj index f722d42dc429..e2aa5aa5c3f5 100644 --- a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/SampleApp.csproj +++ b/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/SampleApp.csproj @@ -1,11 +1,11 @@ - netcoreapp3.1 + net9.0 - + diff --git a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Utilities/FileHelpers.cs b/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Utilities/FileHelpers.cs index 365326bc4743..3ffddd73aa33 100644 --- a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Utilities/FileHelpers.cs +++ b/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Utilities/FileHelpers.cs @@ -146,39 +146,33 @@ public static async Task ProcessFormFile(IFormFile formFile, return Array.Empty(); } - public static async Task ProcessStreamedFile( + public static async Task ProcessStreamedFile( MultipartSection section, ContentDispositionHeaderValue contentDisposition, - ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit) + ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit, Stream destination) { + var oldLength = destination.Length; try { - using (var memoryStream = new MemoryStream()) - { - await section.Body.CopyToAsync(memoryStream); + await section.Body.CopyToAsync(destination); - // Check if the file is empty or exceeds the size limit. - if (memoryStream.Length == 0) - { - modelState.AddModelError("File", "The file is empty."); - } - else if (memoryStream.Length > sizeLimit) - { - var megabyteSizeLimit = sizeLimit / 1048576; - modelState.AddModelError("File", - $"The file exceeds {megabyteSizeLimit:N1} MB."); - } - else if (!IsValidFileExtensionAndSignature( - contentDisposition.FileName.Value, memoryStream, - permittedExtensions)) - { - modelState.AddModelError("File", - "The file type isn't permitted or the file's " + - "signature doesn't match the file's extension."); - } - else - { - return memoryStream.ToArray(); - } + // Check if the file is empty or exceeds the size limit. + if (destination.Length == 0) + { + modelState.AddModelError("File", "The file is empty."); + } + else if (destination.Length > sizeLimit) + { + var megabyteSizeLimit = sizeLimit / 1048576; + modelState.AddModelError("File", + $"The file exceeds {megabyteSizeLimit:N1} MB."); + } + else if (!IsValidFileExtensionAndSignature( + contentDisposition.FileName.Value, destination, + permittedExtensions)) + { + modelState.AddModelError("File", + "The file type isn't permitted or the file's " + + "signature doesn't match the file's extension."); } } catch (Exception ex) @@ -187,9 +181,8 @@ public static async Task ProcessStreamedFile( "The upload failed. Please contact the Help Desk " + $" for support. Error: {ex.HResult}"); // Log the exception + destination.SetLength(oldLength); // Reset the stream to its original length } - - return Array.Empty(); } private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, string[] permittedExtensions) @@ -208,7 +201,7 @@ private static bool IsValidFileExtensionAndSignature(string fileName, Stream dat data.Position = 0; - using (var reader = new BinaryReader(data)) + using (var reader = new BinaryReader(data, System.Text.Encoding.UTF8, leaveOpen: true)) { if (ext.Equals(".txt") || ext.Equals(".csv") || ext.Equals(".prn")) { @@ -247,12 +240,10 @@ private static bool IsValidFileExtensionAndSignature(string fileName, Stream dat // for files (when possible) for all file types you intend // to allow on the system and perform the file signature // check. - /* if (!_fileSignature.ContainsKey(ext)) { return true; } - */ // File signature check // -------------------- diff --git a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/appsettings.Development.json b/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/appsettings.Development.json index e203e9407e74..5df4997987d3 100644 --- a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/appsettings.Development.json +++ b/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/appsettings.Development.json @@ -1,9 +1,8 @@ { "Logging": { "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" + "Default": "Warning", + "Microsoft": "Debug" } } } diff --git a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/appsettings.json b/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/appsettings.json index 9083deaf76da..1ed7061325ec 100644 --- a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/appsettings.json +++ b/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/appsettings.json @@ -7,6 +7,6 @@ } }, "AllowedHosts": "*", - "StoredFilesPath": "c:\\files", - "FileSizeLimit": 2097152 + "StoredFilesPath": "D:\\tmp\\LargeFileUpload", + "FileSizeLimit": 6442450944 }