--- title: Upload files in ASP.NET Core author: guardrex description: How to use model binding and streaming to upload files in ASP.NET Core MVC. monikerRange: '>= aspnetcore-2.1' ms.author: riande ms.custom: mvc ms.date: 10/02/2019 uid: mvc/models/file-uploads --- # Upload files in ASP.NET Core By [Luke Latham](https://github.com/guardrex), [Steve Smith](https://ardalis.com/), and [Rutger Storm](https://github.com/rutix) ::: moniker range=">= aspnetcore-3.0" ASP.NET Core supports uploading one or more files using buffered model binding for smaller files and unbuffered streaming for larger files. [View or download sample code](https://github.com/aspnet/AspNetCore.Docs/tree/master/aspnetcore/mvc/models/file-uploads/samples/) ([how to download](xref:index#how-to-download-a-sample)) ## Security considerations Use caution when providing users with the ability to upload files to a server. Attackers may attempt to: * Execute [denial of service](/windows-hardware/drivers/ifs/denial-of-service) attacks. * Upload viruses or malware. * Compromise networks and servers in other ways. Security steps that reduce the likelihood of a successful attack are: * Upload files to a dedicated file upload area on the system, preferably to a non-system drive. Using a dedicated location makes it easier to impose security restrictions on uploaded files. Disable execute permissions on the file upload location.† * Never persist uploaded files in the same directory tree as the app.† * Use a safe file name determined by the app. Don't use a file name provided by the user or the untrusted file name of the uploaded file.† To display an untrusted file name in a UI or in a logging message, HTML-encode the value. * Only allow a specific set of approved file extensions.† * Check the file format signature to prevent a user from uploading a masqueraded file.† For example, don't permit a user to upload an *.exe* file with a *.txt* extension. * Verify that client-side checks are also performed on the server.† Client-side checks are easy to circumvent. * Check the size of an uploaded file and prevent uploads that are larger than expected.† * When files shouldn't be overwritten by an uploaded file with the same name, check the file name against the database or physical storage before uploading the file. * **Run a virus/malware scanner on uploaded content before the file is stored.** †The sample app demonstrates an approach that meets the criteria. > [!WARNING] > Uploading malicious code to a system is frequently the first step to executing code that can: > > * Completely gain control of a system. > * Overload a system with the result that the system crashes. > * Compromise user or system data. > * Apply graffiti to a public UI. > > For information on reducing the attack surface area when accepting files from users, see the following resources: > > * [Unrestricted File Upload](https://www.owasp.org/index.php/Unrestricted_File_Upload) > * [Azure Security: Ensure appropriate controls are in place when accepting files from users](/azure/security/azure-security-threat-modeling-tool-input-validation#controls-users) For more information on implementing security measures, including examples from the sample app, see the [Validation](#validation) section. ## Storage scenarios Common storage options for files include: * Database * For small file uploads, a database is often faster than physical storage (file system or network share) options. * A database is often more convenient than physical storage options because retrieval of a database record for user data can concurrently supply the file content (for example, an avatar image). * A database is potentially less expensive than using a data storage service. * Physical storage (file system or network share) * For large file uploads: * Database limits may restrict the size of the upload. * Physical storage is often less economical than storage in a database. * Physical storage is potentially less expensive than using a data storage service. * The app's process must have read and write permissions to the storage location. **Never grant execute permission.** * Data storage service (for example, [Azure Blob Storage](https://azure.microsoft.com/services/storage/blobs/)) * Services usually offer improved scalability and resiliency over on-premises solutions that are usually subject to single points of failure. * Services are potentially lower cost in large storage infrastructure scenarios. For more information, see [Quickstart: Use .NET to create a blob in object storage](/azure/storage/blobs/storage-quickstart-blobs-dotnet). The topic demonstrates , but can be used to save a to blob storage when working with a . ## File upload scenarios Two general approaches for uploading files are buffering and streaming. **Buffering** The entire file is read into an , which is a C# representation of the file used to process or save the file. The resources (disk, memory) used by file uploads depend on the number and size of concurrent file uploads. If an app attempts to buffer too many uploads, the site crashes when it runs out of memory or disk space. If the size or frequency of file uploads is exhausting app resources, use streaming. > [!NOTE] > Any single buffered file exceeding 64 KB is moved from memory to a temp file on disk. Buffering small files is covered in the following sections of this topic: * [Physical storage](#upload-small-files-with-buffered-model-binding-to-physical-storage) * [Database](#upload-small-files-with-buffered-model-binding-to-a-database) **Streaming** The file is received from a multipart request and directly processed or saved by the app. Streaming doesn't improve performance significantly. Streaming reduces the demands for memory or disk space when uploading files. Streaming large files is covered in the [Upload large files with streaming](#upload-large-files-with-streaming) section. ### Upload small files with buffered model binding to physical storage To upload small files, use a multipart form or construct a POST request using JavaScript. The following example demonstrates the use of a Razor Pages form to upload a single file (*Pages/BufferedSingleFileUploadPhysical.cshtml* in the sample app): ```cshtml
``` The following example is analogous to the prior example except that: * JavaScript's ([Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API)) is used to submit the form's data. * There's no validation. ```cshtml
``` To perform the form POST in JavaScript for clients that [don't support the Fetch API](https://caniuse.com/#feat=fetch), use one of the following approaches: * Use a Fetch Polyfill (for example, [window.fetch polyfill (github/fetch)](https://github.com/github/fetch)). * Use `XMLHttpRequest`. For example: ```javascript ``` In order to support file uploads, HTML forms must specify an encoding type (`enctype`) of `multipart/form-data`. For a `files` input element to support uploading multiple files provide the `multiple` attribute on the `` element: ```cshtml ``` The individual files uploaded to the server can be accessed through [Model Binding](xref:mvc/models/model-binding) using . The sample app demonstrates multiple buffered file uploads for database and physical storage scenarios. > [!WARNING] > Don't rely on or trust the `FileName` property of without validation. The `FileName` property should only be used for display purposes and only after HTML encoding the value. > > The examples provided thus far don't take into account security considerations. Additional information is provided by the following sections and the [sample app](https://github.com/aspnet/AspNetCore.Docs/tree/master/aspnetcore/mvc/models/file-uploads/samples/): > > * [Security considerations](#security-considerations) > * [Validation](#validation) When uploading files using model binding and , the action method can accept: * A single . * Any of the following collections that represent several files: * * \<> * [List](xref:System.Collections.Generic.List`1)\<> > [!NOTE] > Binding matches form files by name. For example, the HTML `name` value in `` must match the C# parameter/property bound (`FormFile`). For more information, see the [Match name attribute value to parameter name of POST method](#match-name-attribute-value-to-parameter-name-of-post-method) section. The following example: * Loops through one or more uploaded files. * Uses [Path.GetTempFileName](xref:System.IO.Path.GetTempFileName*) to return a full path for a file, including the file name. * Saves the files to the local file system using a file name generated by the app. * Returns the total number and size of files uploaded. ```csharp public async Task OnPostUploadAsync(List files) { long size = files.Sum(f => f.Length); foreach (var formFile in files) { if (formFile.Length > 0) { var filePath = Path.GetTempFileName(); using (var stream = System.IO.File.Create(filePath)) { await formFile.CopyToAsync(stream); } } } // Process uploaded files // Don't rely on or trust the FileName property without validation. return Ok(new { count = files.Count, size, filePath }); } ``` Use `Path.GetRandomFileName` to generate a file name without a path. In the following example, the path is obtained from configuration: ```csharp foreach (var formFile in files) { if (formFile.Length > 0) { var filePath = Path.Combine(_config["StoredFilesPath"], Path.GetRandomFileName()); using (var stream = System.IO.File.Create(filePath)) { await formFile.CopyToAsync(stream); } } } ``` The path passed to the *must* include the file name. If the file name isn't provided, an is thrown at runtime. Files uploaded using the technique are buffered in memory or on disk on the server before processing. Inside the action method, the contents are accessible as a . In addition to the local file system, files can be saved to a network share or to a file storage service, such as [Azure Blob storage](/azure/visual-studio/vs-storage-aspnet5-getting-started-blobs). For another example that loops over multiple files for upload and uses safe file names, see *Pages/BufferedMultipleFileUploadPhysical.cshtml.cs* in the sample app. > [!WARNING] > [Path.GetTempFileName](xref:System.IO.Path.GetTempFileName*) throws an if more than 65,535 files are created without deleting previous temporary files. The limit of 65,535 files is a per-server limit. For more information on this limit on Windows OS, see the remarks in the following topics: > > * [GetTempFileNameA function](/windows/desktop/api/fileapi/nf-fileapi-gettempfilenamea#remarks) > * ### Upload small files with buffered model binding to a database To store binary file data in a database using [Entity Framework](/ef/core/index), define a array property on the entity: ```csharp public class AppFile { public int Id { get; set; } public byte[] Content { get; set; } } ``` Specify a page model property for the class that includes an : ```csharp public class BufferedSingleFileUploadDbModel : PageModel { ... [BindProperty] public BufferedSingleFileUploadDb FileUpload { get; set; } ... } public class BufferedSingleFileUploadDb { [Required] [Display(Name="File")] public IFormFile FormFile { get; set; } } ``` > [!NOTE] > can be used directly as an action method parameter or as a bound model property. The prior example uses a bound model property. The `FileUpload` is used in the Razor Pages form: ```cshtml
``` When the form is POSTed to the server, copy the to a stream and save it as a byte array in the database. In the following example, `_dbContext` stores the app's database context: ```csharp public async Task OnPostUploadAsync() { using (var memoryStream = new MemoryStream()) { await FileUpload.FormFile.CopyToAsync(memoryStream); // Upload the file if less than 2 MB if (memoryStream.Length < 2097152) { var file = new AppFile() { Content = memoryStream.ToArray() }; _dbContext.File.Add(file); await _dbContext.SaveChangesAsync(); } else { ModelState.AddModelError("File", "The file is too large."); } } return Page(); } ``` The preceding example is similar to a scenario demonstrated in the sample app: * *Pages/BufferedSingleFileUploadDb.cshtml* * *Pages/BufferedSingleFileUploadDb.cshtml.cs* > [!WARNING] > Use caution when storing binary data in relational databases, as it can adversely impact performance. > > Don't rely on or trust the `FileName` property of without validation. The `FileName` property should only be used for display purposes and only after HTML encoding. > > The examples provided don't take into account security considerations. Additional information is provided by the following sections and the [sample app](https://github.com/aspnet/AspNetCore.Docs/tree/master/aspnetcore/mvc/models/file-uploads/samples/): > > * [Security considerations](#security-considerations) > * [Validation](#validation) ### Upload large files with streaming The following example demonstrates how to use JavaScript to stream a file to a controller action. The file's antiforgery token is generated using a custom filter attribute and passed to the client HTTP headers instead of in the request body. Because the action method processes the uploaded data directly, form model binding is disabled by another custom filter. Within the action, the form's contents are read using a `MultipartReader`, which reads each individual `MultipartSection`, processing the file or storing the contents as appropriate. After the multipart sections are read, the action performs its own model binding. The initial page response loads the form and saves an antiforgery token in a cookie (via the `GenerateAntiforgeryTokenCookieAttribute` attribute). The attribute uses ASP.NET Core's built-in [antiforgery support](xref:security/anti-request-forgery) to set a cookie with a request token: [!code-csharp[](file-uploads/samples/3.x/SampleApp/Filters/Antiforgery.cs?name=snippet_GenerateAntiforgeryTokenCookieAttribute)] The `DisableFormValueModelBindingAttribute` is used to disable model binding: [!code-csharp[](file-uploads/samples/3.x/SampleApp/Filters/ModelBinding.cs?name=snippet_DisableFormValueModelBindingAttribute)] In the sample app, `GenerateAntiforgeryTokenCookieAttribute` and `DisableFormValueModelBindingAttribute` are applied as filters to the page application models of `/StreamedSingleFileUploadDb` and `/StreamedSingleFileUploadPhysical` in `Startup.ConfigureServices` using [Razor Pages conventions](xref:razor-pages/razor-pages-conventions): [!code-csharp[](file-uploads/samples/3.x/SampleApp/Startup.cs?name=snippet_AddRazorPages&highlight=8-11,17-20)] Since model binding doesn't read the form, parameters that are bound from the form don't bind (query, route, and header continue to work). The action method works directly with the `Request` property. A `MultipartReader` is used to read each section. Key/value data is stored in a `KeyValueAccumulator`. After the multipart sections are read, the contents of the `KeyValueAccumulator` are used to bind the form data to a model type. The complete `StreamingController.UploadDatabase` method for streaming to a database with EF Core: [!code-csharp[](file-uploads/samples/3.x/SampleApp/Controllers/StreamingController.cs?name=snippet_UploadDatabase)] `MultipartRequestHelper` (*Utilities/MultipartRequestHelper.cs*): [!code-csharp[](file-uploads/samples/3.x/SampleApp/Utilities/MultipartRequestHelper.cs)] The complete `StreamingController.UploadPhysical` method for streaming to a physical location: [!code-csharp[](file-uploads/samples/3.x/SampleApp/Controllers/StreamingController.cs?name=snippet_UploadPhysical)] In the sample app, validation checks are handled by `FileHelpers.ProcessStreamedFile`. ## Validation The sample app's `FileHelpers` class demonstrates a several checks for buffered and streamed file uploads. For processing buffered file uploads in the sample app, see the `ProcessFormFile` method in the *Utilities/FileHelpers.cs* file. For processing streamed files, see the `ProcessStreamedFile` method in the same file. > [!WARNING] > The validation processing methods demonstrated in the sample app don't scan the content of uploaded files. In most production scenarios, a virus/malware scanner API is used on the file before making the file available to users or other systems. > > Although the topic sample provides a working example of validation techniques, don't implement the `FileHelpers` class in a production app unless you: > > * Fully understand the implementation. > * Modify the implementation as appropriate for the app's environment and specifications. > > **Never indiscriminately implement security code in an app without addressing these requirements.** ### Content validation **Use a third party virus/malware scanning API on uploaded content.** Scanning files is demanding on server resources in high volume scenarios. If request processing performance is diminished due to file scanning, consider offloading the scanning work to a [background service](xref:fundamentals/host/hosted-services), possibly a service running on a server different from the app's server. Typically, uploaded files are held in a quarantined area until the background virus scanner checks them. When a file passes, the file is moved to the normal file storage location. These steps are usually performed in conjunction with a database record that indicates the scanning status of a file. By using such an approach, the app and app server remain focused on responding to requests. ### File extension validation The uploaded file's extension should be checked against a list of permitted extensions. For example: ```csharp private string[] permittedExtensions = { ".txt", ".pdf" }; var ext = Path.GetExtension(uploadedFileName).ToLowerInvariant(); if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext)) { // The extension is invalid ... discontinue processing the file } ``` ### File signature validation A file's signature is determined by the first few bytes at the start of a file. These bytes can be used to indicate if the extension matches the content of the file. The sample app checks file signatures for a few common file types. In the following example, the file signature for a JPEG image is checked against the file: ```csharp private static readonly Dictionary> _fileSignature = new Dictionary> { { ".jpeg", new List { new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 }, new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 }, } }, }; using (var reader = new BinaryReader(uploadedFileData)) { var signatures = _fileSignature[ext]; var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length)); return signatures.Any(signature => headerBytes.Take(signature.Length).SequenceEqual(signature)); } ``` To obtain additional file signatures, see the [File Signatures Database](https://www.filesignatures.net/) and official file specifications. ### 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 [Path.GetRandomFileName](xref:System.IO.Path.GetRandomFileName*) or [Path.GetTempFileName](xref:System.IO.Path.GetTempFileName*) 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 file name content 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. ### Size validation Limit the size of uploaded files. In the sample app, the size of the file is limited to 2 MB (indicated in bytes). The limit is supplied via [Configuration](xref:fundamentals/configuration/index) from the *appsettings.json* file: ```json { "FileSizeLimit": 2097152 } ``` The `FileSizeLimit` is injected into `PageModel` classes: ```csharp public class BufferedSingleFileUploadPhysicalModel : PageModel { private readonly long _fileSizeLimit; public BufferedSingleFileUploadPhysicalModel(IConfiguration config) { _fileSizeLimit = config.GetValue("FileSizeLimit"); } ... } ``` When a file size exceeds the limit, the file is rejected: ```csharp if (formFile.Length > _fileSizeLimit) { // The file is too large ... discontinue processing the file } ``` ### Match name attribute value to parameter name of POST method In non-Razor forms that POST form data or use JavaScript's `FormData` directly, the name specified in the form's element or `FormData` must match the name of the parameter in the controller's action. In the following example: * When using an `` element, the `name` attribute is set to the value `battlePlans`: ```html ``` * When using `FormData` in JavaScript, the name is set to the value `battlePlans`: ```javascript var formData = new FormData(); for (var file in files) { formData.append("battlePlans", file, file.name); } ``` Use a matching name for the parameter of the C# method (`battlePlans`): * For a Razor Pages page handler method named `Upload`: ```csharp public async Task OnPostUploadAsync(List battlePlans) ``` * For an MVC POST controller action method: ```csharp public async Task Post(List battlePlans) ``` ## Server and app configuration ### Multipart body length limit sets the limit for the length of each multipart body. Form sections that exceed this limit throw an when parsed. The default is 134,217,728 (128 MB). Customize the limit using the setting in `Startup.ConfigureServices`: ```csharp public void ConfigureServices(IServiceCollection services) { services.Configure(options => { // Set the limit to 256 MB options.MultipartBodyLengthLimit = 268435456; }); } ``` is used to set the for a single page or action. In a Razor Pages app, apply the filter with a [convention](xref:razor-pages/razor-pages-conventions) in `Startup.ConfigureServices`: ```csharp services.AddRazorPages() .AddRazorPagesOptions(options => { options.Conventions .AddPageApplicationModelConvention("/FileUploadPage", model.Filters.Add( new RequestFormLimitsAttribute() { // Set the limit to 256 MB MultipartBodyLengthLimit = 268435456 }); }); ``` In a Razor Pages app or an MVC app, apply the filter to the page model or action method: ```csharp // Set the limit to 256 MB [RequestFormLimits(MultipartBodyLengthLimit = 268435456)] public class BufferedSingleFileUploadPhysicalModel : PageModel { ... } ``` ### Kestrel maximum request body size For apps hosted by Kestrel, the default maximum request body size is 30,000,000 bytes, which is approximately 28.6 MB. Customize the limit using the [MaxRequestBodySize](xref:fundamentals/servers/kestrel#maximum-request-body-size) Kestrel server option: ```csharp public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureKestrel((context, options) => { // Handle requests up to 50 MB options.Limits.MaxRequestBodySize = 52428800; }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); ``` is used to set the [MaxRequestBodySize](xref:fundamentals/servers/kestrel#maximum-request-body-size) for a single page or action. In a Razor Pages app, apply the filter with a [convention](xref:razor-pages/razor-pages-conventions) in `Startup.ConfigureServices`: ```csharp services.AddRazorPages() .AddRazorPagesOptions(options => { options.Conventions .AddPageApplicationModelConvention("/FileUploadPage", model => { // Handle requests up to 50 MB model.Filters.Add( new RequestSizeLimitAttribute(52428800)); }); }); ``` In a Razor pages app or an MVC app, apply the filter to the page handler class or action method: ```csharp // Handle requests up to 50 MB [RequestSizeLimit(52428800)] public class BufferedSingleFileUploadPhysicalModel : PageModel { ... } ``` The `RequestSizeLimitAttribute` can also be applied using the [@attribute](xref:mvc/views/razor#attribute) Razor directive: ```cshtml @attribute [RequestSizeLimitAttribute(52428800)] ``` ### Other Kestrel limits Other Kestrel limits may apply for apps hosted by Kestrel: * [Maximum client connections](xref:fundamentals/servers/kestrel#maximum-client-connections) * [Request and response data rates](xref:fundamentals/servers/kestrel#minimum-request-body-data-rate) ### IIS content length limit The default request limit (`maxAllowedContentLength`) is 30,000,000 bytes, which is approximately 28.6MB. Customize the limit in the *web.config* file: ```xml ``` This setting only applies to IIS. The behavior doesn't occur by default when hosting on Kestrel. For more information, see [Request Limits \](/iis/configuration/system.webServer/security/requestFiltering/requestLimits/). Limitations in the ASP.NET Core Module or presence of the IIS Request Filtering Module may limit uploads to either 2 or 4 GB. For more information, see [Unable to upload file greater than 2GB in size (aspnet/AspNetCore #2711)](https://github.com/aspnet/AspNetCore/issues/2711). ## Troubleshoot Below are some common problems encountered when working with uploading files and their possible solutions. ### Not Found error when deployed to an IIS server The following error indicates that the uploaded file exceeds the server's configured content length: ``` HTTP 404.13 - Not Found The request filtering module is configured to deny a request that exceeds the request content length. ``` For more information on increasing the limit, see the [IIS content length limit](#iis-content-length-limit) section. ### Connection failure A connection error and a reset server connection probably indicates that the uploaded file exceeds Kestrel's maximum request body size. For more information, see the [Kestrel maximum request body size](#kestrel-maximum-request-body-size) section. Kestrel client connection limits may also require adjustment. ### Null Reference Exception with IFormFile If the controller is accepting uploaded files using but the value is `null`, confirm that the HTML form is specifying an `enctype` value of `multipart/form-data`. If this attribute isn't set on the `
` element, the file upload doesn't occur and any bound arguments are `null`. Also confirm that the [upload naming in form data matches the app's naming](#match-name-attribute-value-to-parameter-name-of-post-method). ::: moniker-end ::: moniker range="< aspnetcore-3.0" ASP.NET Core supports uploading one or more files using buffered model binding for smaller files and unbuffered streaming for larger files. [View or download sample code](https://github.com/aspnet/AspNetCore.Docs/tree/master/aspnetcore/mvc/models/file-uploads/samples/) ([how to download](xref:index#how-to-download-a-sample)) ## Security considerations Use caution when providing users with the ability to upload files to a server. Attackers may attempt to: * Execute [denial of service](/windows-hardware/drivers/ifs/denial-of-service) attacks. * Upload viruses or malware. * Compromise networks and servers in other ways. Security steps that reduce the likelihood of a successful attack are: * Upload files to a dedicated file upload area on the system, preferably to a non-system drive. Using a dedicated location makes it easier to impose security restrictions on uploaded files. Disable execute permissions on the file upload location.† * Never persist uploaded files in the same directory tree as the app.† * Use a safe file name determined by the app. Don't use a file name provided by the user or the untrusted file name of the uploaded file.† To display an untrusted file name in a UI or in a logging message, HTML-encode the value. * Only allow a specific set of approved file extensions.† * Check the file format signature to prevent a user from uploading a masqueraded file.† For example, don't permit a user to upload an *.exe* file with a *.txt* extension. * Verify that client-side checks are also performed on the server.† Client-side checks are easy to circumvent. * Check the size of an uploaded file and prevent uploads that are larger than expected.† * When files shouldn't be overwritten by an uploaded file with the same name, check the file name against the database or physical storage before uploading the file. * **Run a virus/malware scanner on uploaded content before the file is stored.** †The sample app demonstrates an approach that meets the criteria. > [!WARNING] > Uploading malicious code to a system is frequently the first step to executing code that can: > > * Completely gain control of a system. > * Overload a system with the result that the system crashes. > * Compromise user or system data. > * Apply graffiti to a public UI. > > For information on reducing the attack surface area when accepting files from users, see the following resources: > > * [Unrestricted File Upload](https://www.owasp.org/index.php/Unrestricted_File_Upload) > * [Azure Security: Ensure appropriate controls are in place when accepting files from users](/azure/security/azure-security-threat-modeling-tool-input-validation#controls-users) For more information on implementing security measures, including examples from the sample app, see the [Validation](#validation) section. ## Storage scenarios Common storage options for files include: * Database * For small file uploads, a database is often faster than physical storage (file system or network share) options. * A database is often more convenient than physical storage options because retrieval of a database record for user data can concurrently supply the file content (for example, an avatar image). * A database is potentially less expensive than using a data storage service. * Physical storage (file system or network share) * For large file uploads: * Database limits may restrict the size of the upload. * Physical storage is often less economical than storage in a database. * Physical storage is potentially less expensive than using a data storage service. * The app's process must have read and write permissions to the storage location. **Never grant execute permission.** * Data storage service (for example, [Azure Blob Storage](https://azure.microsoft.com/services/storage/blobs/)) * Services usually offer improved scalability and resiliency over on-premises solutions that are usually subject to single points of failure. * Services are potentially lower cost in large storage infrastructure scenarios. For more information, see [Quickstart: Use .NET to create a blob in object storage](/azure/storage/blobs/storage-quickstart-blobs-dotnet). The topic demonstrates , but can be used to save a to blob storage when working with a . ## File upload scenarios Two general approaches for uploading files are buffering and streaming. **Buffering** The entire file is read into an , which is a C# representation of the file used to process or save the file. The resources (disk, memory) used by file uploads depend on the number and size of concurrent file uploads. If an app attempts to buffer too many uploads, the site crashes when it runs out of memory or disk space. If the size or frequency of file uploads is exhausting app resources, use streaming. > [!NOTE] > Any single buffered file exceeding 64 KB is moved from memory to a temp file on disk. Buffering small files is covered in the following sections of this topic: * [Physical storage](#upload-small-files-with-buffered-model-binding-to-physical-storage) * [Database](#upload-small-files-with-buffered-model-binding-to-a-database) **Streaming** The file is received from a multipart request and directly processed or saved by the app. Streaming doesn't improve performance significantly. Streaming reduces the demands for memory or disk space when uploading files. Streaming large files is covered in the [Upload large files with streaming](#upload-large-files-with-streaming) section. ### Upload small files with buffered model binding to physical storage To upload small files, use a multipart form or construct a POST request using JavaScript. The following example demonstrates the use of a Razor Pages form to upload a single file (*Pages/BufferedSingleFileUploadPhysical.cshtml* in the sample app): ```cshtml
``` The following example is analogous to the prior example except that: * JavaScript's ([Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API)) is used to submit the form's data. * There's no validation. ```cshtml
``` To perform the form POST in JavaScript for clients that [don't support the Fetch API](https://caniuse.com/#feat=fetch), use one of the following approaches: * Use a Fetch Polyfill (for example, [window.fetch polyfill (github/fetch)](https://github.com/github/fetch)). * Use `XMLHttpRequest`. For example: ```javascript ``` In order to support file uploads, HTML forms must specify an encoding type (`enctype`) of `multipart/form-data`. For a `files` input element to support uploading multiple files provide the `multiple` attribute on the `` element: ```cshtml ``` The individual files uploaded to the server can be accessed through [Model Binding](xref:mvc/models/model-binding) using . The sample app demonstrates multiple buffered file uploads for database and physical storage scenarios. > [!WARNING] > Don't rely on or trust the `FileName` property of without validation. The `FileName` property should only be used for display purposes and only after HTML encoding the value. > > The examples provided thus far don't take into account security considerations. Additional information is provided by the following sections and the [sample app](https://github.com/aspnet/AspNetCore.Docs/tree/master/aspnetcore/mvc/models/file-uploads/samples/): > > * [Security considerations](#security-considerations) > * [Validation](#validation) When uploading files using model binding and , the action method can accept: * A single . * Any of the following collections that represent several files: * * \<> * [List](xref:System.Collections.Generic.List`1)\<> > [!NOTE] > Binding matches form files by name. For example, the HTML `name` value in `` must match the C# parameter/property bound (`FormFile`). For more information, see the [Match name attribute value to parameter name of POST method](#match-name-attribute-value-to-parameter-name-of-post-method) section. The following example: * Loops through one or more uploaded files. * Uses [Path.GetTempFileName](xref:System.IO.Path.GetTempFileName*) to return a full path for a file, including the file name. * Saves the files to the local file system using a file name generated by the app. * Returns the total number and size of files uploaded. ```csharp public async Task OnPostUploadAsync(List files) { long size = files.Sum(f => f.Length); foreach (var formFile in files) { if (formFile.Length > 0) { var filePath = Path.GetTempFileName(); using (var stream = System.IO.File.Create(filePath)) { await formFile.CopyToAsync(stream); } } } // Process uploaded files // Don't rely on or trust the FileName property without validation. return Ok(new { count = files.Count, size, filePath }); } ``` Use `Path.GetRandomFileName` to generate a file name without a path. In the following example, the path is obtained from configuration: ```csharp foreach (var formFile in files) { if (formFile.Length > 0) { var filePath = Path.Combine(_config["StoredFilesPath"], Path.GetRandomFileName()); using (var stream = System.IO.File.Create(filePath)) { await formFile.CopyToAsync(stream); } } } ``` The path passed to the *must* include the file name. If the file name isn't provided, an is thrown at runtime. Files uploaded using the technique are buffered in memory or on disk on the server before processing. Inside the action method, the contents are accessible as a . In addition to the local file system, files can be saved to a network share or to a file storage service, such as [Azure Blob storage](/azure/visual-studio/vs-storage-aspnet5-getting-started-blobs). For another example that loops over multiple files for upload and uses safe file names, see *Pages/BufferedMultipleFileUploadPhysical.cshtml.cs* in the sample app. > [!WARNING] > [Path.GetTempFileName](xref:System.IO.Path.GetTempFileName*) throws an if more than 65,535 files are created without deleting previous temporary files. The limit of 65,535 files is a per-server limit. For more information on this limit on Windows OS, see the remarks in the following topics: > > * [GetTempFileNameA function](/windows/desktop/api/fileapi/nf-fileapi-gettempfilenamea#remarks) > * ### Upload small files with buffered model binding to a database To store binary file data in a database using [Entity Framework](/ef/core/index), define a array property on the entity: ```csharp public class AppFile { public int Id { get; set; } public byte[] Content { get; set; } } ``` Specify a page model property for the class that includes an : ```csharp public class BufferedSingleFileUploadDbModel : PageModel { ... [BindProperty] public BufferedSingleFileUploadDb FileUpload { get; set; } ... } public class BufferedSingleFileUploadDb { [Required] [Display(Name="File")] public IFormFile FormFile { get; set; } } ``` > [!NOTE] > can be used directly as an action method parameter or as a bound model property. The prior example uses a bound model property. The `FileUpload` is used in the Razor Pages form: ```cshtml
``` When the form is POSTed to the server, copy the to a stream and save it as a byte array in the database. In the following example, `_dbContext` stores the app's database context: ```csharp public async Task OnPostUploadAsync() { using (var memoryStream = new MemoryStream()) { await FileUpload.FormFile.CopyToAsync(memoryStream); // Upload the file if less than 2 MB if (memoryStream.Length < 2097152) { var file = new AppFile() { Content = memoryStream.ToArray() }; _dbContext.File.Add(file); await _dbContext.SaveChangesAsync(); } else { ModelState.AddModelError("File", "The file is too large."); } } return Page(); } ``` The preceding example is similar to a scenario demonstrated in the sample app: * *Pages/BufferedSingleFileUploadDb.cshtml* * *Pages/BufferedSingleFileUploadDb.cshtml.cs* > [!WARNING] > Use caution when storing binary data in relational databases, as it can adversely impact performance. > > Don't rely on or trust the `FileName` property of without validation. The `FileName` property should only be used for display purposes and only after HTML encoding. > > The examples provided don't take into account security considerations. Additional information is provided by the following sections and the [sample app](https://github.com/aspnet/AspNetCore.Docs/tree/master/aspnetcore/mvc/models/file-uploads/samples/): > > * [Security considerations](#security-considerations) > * [Validation](#validation) ### Upload large files with streaming The following example demonstrates how to use JavaScript to stream a file to a controller action. The file's antiforgery token is generated using a custom filter attribute and passed to the client HTTP headers instead of in the request body. Because the action method processes the uploaded data directly, form model binding is disabled by another custom filter. Within the action, the form's contents are read using a `MultipartReader`, which reads each individual `MultipartSection`, processing the file or storing the contents as appropriate. After the multipart sections are read, the action performs its own model binding. The initial page response loads the form and saves an antiforgery token in a cookie (via the `GenerateAntiforgeryTokenCookieAttribute` attribute). The attribute uses ASP.NET Core's built-in [antiforgery support](xref:security/anti-request-forgery) to set a cookie with a request token: [!code-csharp[](file-uploads/samples/2.x/SampleApp/Filters/Antiforgery.cs?name=snippet_GenerateAntiforgeryTokenCookieAttribute)] The `DisableFormValueModelBindingAttribute` is used to disable model binding: [!code-csharp[](file-uploads/samples/2.x/SampleApp/Filters/ModelBinding.cs?name=snippet_DisableFormValueModelBindingAttribute)] In the sample app, `GenerateAntiforgeryTokenCookieAttribute` and `DisableFormValueModelBindingAttribute` are applied as filters to the page application models of `/StreamedSingleFileUploadDb` and `/StreamedSingleFileUploadPhysical` in `Startup.ConfigureServices` using [Razor Pages conventions](xref:razor-pages/razor-pages-conventions): [!code-csharp[](file-uploads/samples/2.x/SampleApp/Startup.cs?name=snippet_AddMvc&highlight=8-11,17-20)] Since model binding doesn't read the form, parameters that are bound from the form don't bind (query, route, and header continue to work). The action method works directly with the `Request` property. A `MultipartReader` is used to read each section. Key/value data is stored in a `KeyValueAccumulator`. After the multipart sections are read, the contents of the `KeyValueAccumulator` are used to bind the form data to a model type. The complete `StreamingController.UploadDatabase` method for streaming to a database with EF Core: [!code-csharp[](file-uploads/samples/2.x/SampleApp/Controllers/StreamingController.cs?name=snippet_UploadDatabase)] `MultipartRequestHelper` (*Utilities/MultipartRequestHelper.cs*): [!code-csharp[](file-uploads/samples/2.x/SampleApp/Utilities/MultipartRequestHelper.cs)] The complete `StreamingController.UploadPhysical` method for streaming to a physical location: [!code-csharp[](file-uploads/samples/2.x/SampleApp/Controllers/StreamingController.cs?name=snippet_UploadPhysical)] In the sample app, validation checks are handled by `FileHelpers.ProcessStreamedFile`. ## Validation The sample app's `FileHelpers` class demonstrates a several checks for buffered and streamed file uploads. For processing buffered file uploads in the sample app, see the `ProcessFormFile` method in the *Utilities/FileHelpers.cs* file. For processing streamed files, see the `ProcessStreamedFile` method in the same file. > [!WARNING] > The validation processing methods demonstrated in the sample app don't scan the content of uploaded files. In most production scenarios, a virus/malware scanner API is used on the file before making the file available to users or other systems. > > Although the topic sample provides a working example of validation techniques, don't implement the `FileHelpers` class in a production app unless you: > > * Fully understand the implementation. > * Modify the implementation as appropriate for the app's environment and specifications. > > **Never indiscriminately implement security code in an app without addressing these requirements.** ### Content validation **Use a third party virus/malware scanning API on uploaded content.** Scanning files is demanding on server resources in high volume scenarios. If request processing performance is diminished due to file scanning, consider offloading the scanning work to a [background service](xref:fundamentals/host/hosted-services), possibly a service running on a server different from the app's server. Typically, uploaded files are held in a quarantined area until the background virus scanner checks them. When a file passes, the file is moved to the normal file storage location. These steps are usually performed in conjunction with a database record that indicates the scanning status of a file. By using such an approach, the app and app server remain focused on responding to requests. ### File extension validation The uploaded file's extension should be checked against a list of permitted extensions. For example: ```csharp private string[] permittedExtensions = { ".txt", ".pdf" }; var ext = Path.GetExtension(uploadedFileName).ToLowerInvariant(); if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext)) { // The extension is invalid ... discontinue processing the file } ``` ### File signature validation A file's signature is determined by the first few bytes at the start of a file. These bytes can be used to indicate if the extension matches the content of the file. The sample app checks file signatures for a few common file types. In the following example, the file signature for a JPEG image is checked against the file: ```csharp private static readonly Dictionary> _fileSignature = new Dictionary> { { ".jpeg", new List { new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 }, new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 }, } }, }; using (var reader = new BinaryReader(uploadedFileData)) { var signatures = _fileSignature[ext]; var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length)); return signatures.Any(signature => headerBytes.Take(signature.Length).SequenceEqual(signature)); } ``` To obtain additional file signatures, see the [File Signatures Database](https://www.filesignatures.net/) and official file specifications. ### 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 [Path.GetRandomFileName](xref:System.IO.Path.GetRandomFileName*) or [Path.GetTempFileName](xref:System.IO.Path.GetTempFileName*) 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 file name content 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. ### Size validation Limit the size of uploaded files. In the sample app, the size of the file is limited to 2 MB (indicated in bytes). The limit is supplied via [Configuration](xref:fundamentals/configuration/index) from the *appsettings.json* file: ```json { "FileSizeLimit": 2097152 } ``` The `FileSizeLimit` is injected into `PageModel` classes: ```csharp public class BufferedSingleFileUploadPhysicalModel : PageModel { private readonly long _fileSizeLimit; public BufferedSingleFileUploadPhysicalModel(IConfiguration config) { _fileSizeLimit = config.GetValue("FileSizeLimit"); } ... } ``` When a file size exceeds the limit, the file is rejected: ```csharp if (formFile.Length > _fileSizeLimit) { // The file is too large ... discontinue processing the file } ``` ### Match name attribute value to parameter name of POST method In non-Razor forms that POST form data or use JavaScript's `FormData` directly, the name specified in the form's element or `FormData` must match the name of the parameter in the controller's action. In the following example: * When using an `` element, the `name` attribute is set to the value `battlePlans`: ```html ``` * When using `FormData` in JavaScript, the name is set to the value `battlePlans`: ```javascript var formData = new FormData(); for (var file in files) { formData.append("battlePlans", file, file.name); } ``` Use a matching name for the parameter of the C# method (`battlePlans`): * For a Razor Pages page handler method named `Upload`: ```csharp public async Task OnPostUploadAsync(List battlePlans) ``` * For an MVC POST controller action method: ```csharp public async Task Post(List battlePlans) ``` ## Server and app configuration ### Multipart body length limit sets the limit for the length of each multipart body. Form sections that exceed this limit throw an when parsed. The default is 134,217,728 (128 MB). Customize the limit using the setting in `Startup.ConfigureServices`: ```csharp public void ConfigureServices(IServiceCollection services) { services.Configure(options => { // Set the limit to 256 MB options.MultipartBodyLengthLimit = 268435456; }); } ``` is used to set the for a single page or action. In a Razor Pages app, apply the filter with a [convention](xref:razor-pages/razor-pages-conventions) in `Startup.ConfigureServices`: ```csharp services.AddMvc() .AddRazorPagesOptions(options => { options.Conventions .AddPageApplicationModelConvention("/FileUploadPage", model.Filters.Add( new RequestFormLimitsAttribute() { // Set the limit to 256 MB MultipartBodyLengthLimit = 268435456 }); }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); ``` In a Razor Pages app or an MVC app, apply the filter to the page model or action method: ```csharp // Set the limit to 256 MB [RequestFormLimits(MultipartBodyLengthLimit = 268435456)] public class BufferedSingleFileUploadPhysicalModel : PageModel { ... } ``` ### Kestrel maximum request body size For apps hosted by Kestrel, the default maximum request body size is 30,000,000 bytes, which is approximately 28.6 MB. Customize the limit using the [MaxRequestBodySize](xref:fundamentals/servers/kestrel#maximum-request-body-size) Kestrel server option: ```csharp public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() .ConfigureKestrel((context, options) => { // Handle requests up to 50 MB options.Limits.MaxRequestBodySize = 52428800; }); ``` is used to set the [MaxRequestBodySize](xref:fundamentals/servers/kestrel#maximum-request-body-size) for a single page or action. In a Razor Pages app, apply the filter with a [convention](xref:razor-pages/razor-pages-conventions) in `Startup.ConfigureServices`: ```csharp services.AddMvc() .AddRazorPagesOptions(options => { options.Conventions .AddPageApplicationModelConvention("/FileUploadPage", model => { // Handle requests up to 50 MB model.Filters.Add( new RequestSizeLimitAttribute(52428800)); }); }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); ``` In a Razor pages app or an MVC app, apply the filter to the page handler class or action method: ```csharp // Handle requests up to 50 MB [RequestSizeLimit(52428800)] public class BufferedSingleFileUploadPhysicalModel : PageModel { ... } ``` ### Other Kestrel limits Other Kestrel limits may apply for apps hosted by Kestrel: * [Maximum client connections](xref:fundamentals/servers/kestrel#maximum-client-connections) * [Request and response data rates](xref:fundamentals/servers/kestrel#minimum-request-body-data-rate) ### IIS content length limit The default request limit (`maxAllowedContentLength`) is 30,000,000 bytes, which is approximately 28.6MB. Customize the limit in the *web.config* file: ```xml ``` This setting only applies to IIS. The behavior doesn't occur by default when hosting on Kestrel. For more information, see [Request Limits \](/iis/configuration/system.webServer/security/requestFiltering/requestLimits/). Limitations in the ASP.NET Core Module or presence of the IIS Request Filtering Module may limit uploads to either 2 or 4 GB. For more information, see [Unable to upload file greater than 2GB in size (aspnet/AspNetCore #2711)](https://github.com/aspnet/AspNetCore/issues/2711). ## Troubleshoot Below are some common problems encountered when working with uploading files and their possible solutions. ### Not Found error when deployed to an IIS server The following error indicates that the uploaded file exceeds the server's configured content length: ``` HTTP 404.13 - Not Found The request filtering module is configured to deny a request that exceeds the request content length. ``` For more information on increasing the limit, see the [IIS content length limit](#iis-content-length-limit) section. ### Connection failure A connection error and a reset server connection probably indicates that the uploaded file exceeds Kestrel's maximum request body size. For more information, see the [Kestrel maximum request body size](#kestrel-maximum-request-body-size) section. Kestrel client connection limits may also require adjustment. ### Null Reference Exception with IFormFile If the controller is accepting uploaded files using but the value is `null`, confirm that the HTML form is specifying an `enctype` value of `multipart/form-data`. If this attribute isn't set on the `
` element, the file upload doesn't occur and any bound arguments are `null`. Also confirm that the [upload naming in form data matches the app's naming](#match-name-attribute-value-to-parameter-name-of-post-method). ::: moniker-end ## Additional resources * [Unrestricted File Upload](https://www.owasp.org/index.php/Unrestricted_File_Upload) * [Azure Security: Security Frame: Input Validation | Mitigations](/azure/security/azure-security-threat-modeling-tool-input-validation) * [Azure Cloud Design Patterns: Valet Key pattern](/azure/architecture/patterns/valet-key)