Skip to content

Commit d84bb14

Browse files
committed
Modify file upload sample to handle very large files
1 parent e626216 commit d84bb14

File tree

6 files changed

+53
-55
lines changed

6 files changed

+53
-55
lines changed

aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Controllers/StreamingController.cs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class StreamingController : Controller
2424
private readonly AppDbContext _context;
2525
private readonly long _fileSizeLimit;
2626
private readonly ILogger<StreamingController> _logger;
27-
private readonly string[] _permittedExtensions = { ".txt" };
27+
private readonly string[] _permittedExtensions = { ".txt", ".vhdx" };
2828
private readonly string _targetFilePath;
2929

3030
// Get the default form options so that we can use them to set the default
@@ -100,9 +100,12 @@ public async Task<IActionResult> UploadDatabase()
100100
trustedFileNameForDisplay = WebUtility.HtmlEncode(
101101
contentDisposition.FileName.Value);
102102

103-
streamedFileContent =
104-
await FileHelpers.ProcessStreamedFile(section, contentDisposition,
105-
ModelState, _permittedExtensions, _fileSizeLimit);
103+
using (var memoryStream = new MemoryStream())
104+
{
105+
await FileHelpers.ProcessStreamedFile(section, contentDisposition,
106+
ModelState, _permittedExtensions, _fileSizeLimit, memoryStream);
107+
streamedFileContent = memoryStream.ToArray();
108+
}
106109

107110
if (!ModelState.IsValid)
108111
{
@@ -269,19 +272,18 @@ public async Task<IActionResult> UploadPhysical()
269272
// For more information, see the topic that accompanies
270273
// this sample.
271274

272-
var streamedFileContent = await FileHelpers.ProcessStreamedFile(
273-
section, contentDisposition, ModelState,
274-
_permittedExtensions, _fileSizeLimit);
275-
276-
if (!ModelState.IsValid)
277-
{
278-
return BadRequest(ModelState);
279-
}
280-
281275
using (var targetStream = System.IO.File.Create(
282276
Path.Combine(_targetFilePath, trustedFileNameForFileStorage)))
283277
{
284-
await targetStream.WriteAsync(streamedFileContent);
278+
279+
await FileHelpers.ProcessStreamedFile(
280+
section, contentDisposition, ModelState,
281+
_permittedExtensions, _fileSizeLimit, targetStream);
282+
283+
if (!ModelState.IsValid)
284+
{
285+
return BadRequest(ModelState);
286+
}
285287

286288
_logger.LogInformation(
287289
"Uploaded file '{TrustedFileNameForDisplay}' saved to " +

aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Program.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Linq;
44
using Microsoft.Extensions.DependencyInjection;
@@ -20,6 +20,12 @@ public static IHostBuilder CreateHostBuilder(string[] args) =>
2020
.ConfigureWebHostDefaults(webBuilder =>
2121
{
2222
webBuilder.UseStartup<Startup>();
23+
webBuilder.ConfigureKestrel(options =>
24+
{
25+
options.Limits.MaxRequestBodySize = 6L << 30; // 6 GB
26+
options.Limits.Http2.InitialStreamWindowSize = 16 << 20; // 16 MB
27+
options.Limits.Http2.MaxFrameSize = (1 << 24) - 1;
28+
});
2329
});
2430
}
2531
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp3.1</TargetFramework>
4+
<TargetFramework>net9.0</TargetFramework>
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.0" />
8+
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.0-rc.1.24451.1" />
99
</ItemGroup>
1010

1111
</Project>

aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Utilities/FileHelpers.cs

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -146,39 +146,33 @@ public static async Task<byte[]> ProcessFormFile<T>(IFormFile formFile,
146146
return Array.Empty<byte>();
147147
}
148148

149-
public static async Task<byte[]> ProcessStreamedFile(
149+
public static async Task ProcessStreamedFile(
150150
MultipartSection section, ContentDispositionHeaderValue contentDisposition,
151-
ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit)
151+
ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit, Stream destination)
152152
{
153+
var oldLength = destination.Length;
153154
try
154155
{
155-
using (var memoryStream = new MemoryStream())
156-
{
157-
await section.Body.CopyToAsync(memoryStream);
156+
await section.Body.CopyToAsync(destination);
158157

159-
// Check if the file is empty or exceeds the size limit.
160-
if (memoryStream.Length == 0)
161-
{
162-
modelState.AddModelError("File", "The file is empty.");
163-
}
164-
else if (memoryStream.Length > sizeLimit)
165-
{
166-
var megabyteSizeLimit = sizeLimit / 1048576;
167-
modelState.AddModelError("File",
168-
$"The file exceeds {megabyteSizeLimit:N1} MB.");
169-
}
170-
else if (!IsValidFileExtensionAndSignature(
171-
contentDisposition.FileName.Value, memoryStream,
172-
permittedExtensions))
173-
{
174-
modelState.AddModelError("File",
175-
"The file type isn't permitted or the file's " +
176-
"signature doesn't match the file's extension.");
177-
}
178-
else
179-
{
180-
return memoryStream.ToArray();
181-
}
158+
// Check if the file is empty or exceeds the size limit.
159+
if (destination.Length == 0)
160+
{
161+
modelState.AddModelError("File", "The file is empty.");
162+
}
163+
else if (destination.Length > sizeLimit)
164+
{
165+
var megabyteSizeLimit = sizeLimit / 1048576;
166+
modelState.AddModelError("File",
167+
$"The file exceeds {megabyteSizeLimit:N1} MB.");
168+
}
169+
else if (!IsValidFileExtensionAndSignature(
170+
contentDisposition.FileName.Value, destination,
171+
permittedExtensions))
172+
{
173+
modelState.AddModelError("File",
174+
"The file type isn't permitted or the file's " +
175+
"signature doesn't match the file's extension.");
182176
}
183177
}
184178
catch (Exception ex)
@@ -187,9 +181,8 @@ public static async Task<byte[]> ProcessStreamedFile(
187181
"The upload failed. Please contact the Help Desk " +
188182
$" for support. Error: {ex.HResult}");
189183
// Log the exception
184+
destination.SetLength(oldLength); // Reset the stream to its original length
190185
}
191-
192-
return Array.Empty<byte>();
193186
}
194187

195188
private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, string[] permittedExtensions)
@@ -208,7 +201,7 @@ private static bool IsValidFileExtensionAndSignature(string fileName, Stream dat
208201

209202
data.Position = 0;
210203

211-
using (var reader = new BinaryReader(data))
204+
using (var reader = new BinaryReader(data, System.Text.Encoding.UTF8, leaveOpen: true))
212205
{
213206
if (ext.Equals(".txt") || ext.Equals(".csv") || ext.Equals(".prn"))
214207
{
@@ -247,12 +240,10 @@ private static bool IsValidFileExtensionAndSignature(string fileName, Stream dat
247240
// for files (when possible) for all file types you intend
248241
// to allow on the system and perform the file signature
249242
// check.
250-
/*
251243
if (!_fileSignature.ContainsKey(ext))
252244
{
253245
return true;
254246
}
255-
*/
256247

257248
// File signature check
258249
// --------------------
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
{
22
"Logging": {
33
"LogLevel": {
4-
"Default": "Debug",
5-
"System": "Information",
6-
"Microsoft": "Information"
4+
"Default": "Warning",
5+
"Microsoft": "Debug"
76
}
87
}
98
}

aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/appsettings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
}
88
},
99
"AllowedHosts": "*",
10-
"StoredFilesPath": "c:\\files",
11-
"FileSizeLimit": 2097152
10+
"StoredFilesPath": "D:\\tmp\\LargeFileUpload",
11+
"FileSizeLimit": 6442450944
1212
}

0 commit comments

Comments
 (0)