In this article, I will explain some file upload insights and show how to upload single or multiples in the app.net razor pages application. I will cover the following points:
Source Code >> GitHub

File Upload Approaches: Buffering and Streaming

Generally, buffering and streaming are the two scenarios to upload files in asp.net.
Buffering upload files by reading entire file into an IFormFile. This upload option increases the demand of resources like disk, memory based on file size and numbers in simultaneous uploads. It is recommended for small size file. Note: the site may crash if we upload too many files using this buffering option.
IFromFile represents a file sent with HttpRequest.
Streaming is another option to upload file which reduces the utilization of resources like disk or memory during the uploads. In this option, the application send file via multipart request and the file is directly saved or processed which does not enhance the performance. It is generally used for large files.

Security concerns during files upload

File upload is always vulnerable if proper security is not applied to applications. File upload is an opportunity for attackers to upload viruses and malware, break the servers and networks security, run denial of service attacks.
To overcome the above issues and reduce the attacks we can take following steps

Storage options for Uploaded File

Commonly, there are three storage choices for files: Physical, Database, Data Storage service
Physical Storage: this storage option means to store in file storage or network share. Mostly, we create a folder and store the file over there. This is quite easy to implement, however, it is always recommended to keep the logs and records in database for user access and view purposes. It is expensive compared to database storage; however, this option allows to store large files which might be restricted in database. Then again, this storage cost will be less than cloud data storage.
Database Storage: This option is quite convenience over the physical storage and is recommended for small files. This is economical compared to both physical and data storage service. It also easy for implementation as we can retrieve the file content from database directly.
Data Storage service (cloud storage service): this storage is highly scalable and recommended for large storage infrastructures. Example: Azure Blog Storage.
Now we will create an asp.net core application with Razor pages to implement file upload.
Prerequisites
Quickly, we will create a new asp.net core 5.0 razor pages project with Visual Studio 2019.
Open Visual studio >> Create a new Project as shown:
File Upload Insights And Upload Single Or Multiple Files In ASP.NET Core Razor Pages
Next, Choose project template ASP.NET Core Web App.
A project template for creating an ASP.NET core application with ASP.NET Razor Pages content.
File Upload Insights And Upload Single Or Multiple Files In ASP.NET Core Razor Pages
Please Note: Here I am selecting ASP.NET Core with Razor pages (not with MVC).
Next screens will give options to give the project Name, Location and solution name as shown:
File Upload Insights And Upload Single Or Multiple Files In ASP.NET Core Razor Pages
Then, we will option to select the target framework. Here, we will .NET 5.0 which is a current version.
File Upload Insights And Upload Single Or Multiple Files In ASP.NET Core Razor Pages
We have successfully created the ASP.NET core 5 web application with Razor pages.
File Upload Insights And Upload Single Or Multiple Files In ASP.NET Core Razor Pages
We can build and run the solution.
Now we will proceed with File upload.

Upload Single File in asp.net core razor pages

In the solution we will modify index page. Currently, we have Index.cshtml (for View) and index.cshtml.cs (For actions or c# code)
We will add a model for file upload in index.cshtml.cs for file upload as:
  1. public class FileUpload
  2. {
  3. [Required]
  4. [Display(Name = "File")]
  5. public IFormFile FormFile { get; set; }
  6. public string SuccessMessage { get; set; }
  7. }
We can keep this model in separate view model as well if you want to reuse the same model for other uploads as well. For simplicity, I am keeping in index page.
We will add following html in razor page i.e. index.cshtml.
  1. <form enctype="multipart/form-data" method="post">
  2. <div class="form-group">
  3. <label class="file">
  4. <input type="file" asp-for="fileUpload.FormFile" multiple aria-label="File browser example">
  5. <span class="file-custom"></span>
  6. </label>
  7. <input asp-page-handler="Upload" class="btn btn-primary" type="submit" value="Upload">
  8. </div>
  9. </form>
We need to bind the FileUpload model as well. Also we will add uplaod action as shown:
  1. [BindProperty]
  2. public FileUpload fileUpload { get; set; }
  3. //upload action as shown:
  4. private string fullPath = System.AppDomain.CurrentDomain.BaseDirectory.ToString() + "UploadImages";
  5. public IActionResult OnPostUpload(FileUpload fileUpload)
  6. {
  7. //Creating upload folder
  8. if (!Directory.Exists(fullPath))
  9. {
  10. Directory.CreateDirectory(fullPath);
  11. }
  12. var formFile = fileUpload.FormFile;
  13. if (formFile.Length > 0)
  14. {
  15. var filePath = Path.Combine(fullPath, formFile.FileName);
  16. using (var stream = System.IO.File.Create(filePath))
  17. {
  18. formFile.CopyToAsync(stream);
  19. }
  20. }
  21. // Process uploaded files
  22. // Don't rely on or trust the FileName property without validation.
  23. ViewData["SuccessMessage"] = formFile.FileName.ToString() + " files uploaded!!";
  24. return Page();
  25. }
Here, I am defining a path for file upload and creating a folder if not exist.
ViewData for showing uploaded file.
Complete index.cshtml
  1. @page
  2. @model IndexModel
  3. @{
  4. ViewData["Title"] = "Home page";
  5. }
  6. <div class="text-center">
  7. <h4 >Rijsat.com | Upload Single or Multiple files</h4>
  8. <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
  9. </div>
  10. <form enctype="multipart/form-data" method="post">
  11. <div class="form-group">
  12. <label class="file">
  13. <input type="file" asp-for="fileUpload.FormFile" aria-label="File browser example">
  14. <span class="file-custom"></span>
  15. </label>
  16. <input asp-page-handler="Upload" class="btn btn-primary" type="submit" value="Upload">
  17. </div>
  18. @{
  19. if (ViewData["SuccessMessage"] != null)
  20. {
  21. <span class="badge badge-success"> @ViewData["SuccessMessage"]</span> }
  22. }
  23. </form>
Compete index.cshtml.cs
  1. using Microsoft.AspNetCore.Http;
  2. using Microsoft.AspNetCore.Mvc;
  3. using Microsoft.AspNetCore.Mvc.RazorPages;
  4. using Microsoft.Extensions.Logging;
  5. using System.ComponentModel.DataAnnotations;
  6. using System.IO;
  7. namespace computervision.aspcore.Pages
  8. {
  9. public class IndexModel : PageModel
  10. {
  11. private readonly ILogger<IndexModel> _logger;
  12. private string fullPath = System.AppDomain.CurrentDomain.BaseDirectory.ToString() + "UploadImages";
  13. public IndexModel(ILogger<IndexModel> logger)
  14. {
  15. _logger = logger;
  16. }
  17. [BindProperty]
  18. public FileUpload fileUpload { get; set; }
  19. public void OnGet()
  20. {
  21. ViewData["SuccessMessage"] = "";
  22. }
  23. public IActionResult OnPostUpload(FileUpload fileUpload)
  24. {
  25. //Creating upload folder
  26. if (!Directory.Exists(fullPath))
  27. {
  28. Directory.CreateDirectory(fullPath);
  29. }
  30. var formFile = fileUpload.FormFile;
  31. var filePath = Path.Combine(fullPath, formFile.FileName);
  32. using (var stream = System.IO.File.Create(filePath))
  33. {
  34. formFile.CopyToAsync(stream);
  35. }
  36. // Process uploaded files
  37. // Don't rely on or trust the FileName property without validation.
  38. ViewData["SuccessMessage"] = formFile.FileName.ToString() + " files uploaded!!";
  39. return Page();
  40. }
  41. }
  42. public class FileUpload
  43. {
  44. [Required]
  45. [Display(Name = "File")]
  46. public IFormFile FormFile { get; set; }
  47. public string SuccessMessage { get; set; }
  48. }
  49. }
Now, we will run the solution and test the upload.
File Upload Insights And Upload Single Or Multiple Files In ASP.NET Core Razor Pages
Select any file and click upload.
File Upload Insights And Upload Single Or Multiple Files In ASP.NET Core Razor Pages
Now we can see the file uploaded successfully. Up to here, we have completed single file upload, subsequently, we will proceed for multiple files.
It quite easy to upload multiple files as well with some modifications.

Upload Multiple files

First, we need to modify the FileUpload Model as shown:
  1. public class FileUpload
  2. {
  3. [Required]
  4. [Display(Name = "File")]
  5. public List<IFormFile> FormFiles { get; set; } // convert to list
  6. public string SuccessMessage { get; set; }
  7. }

Update IFormFile to List<iFormFile>

Then update razor page, add multiple in file upload input type as shown:
  1. <input type="file" asp-for="fileUpload.FormFiles" multiple aria-label="File browser example">
Similarly, we can modify the upload functions as shown:
  1. public IActionResult OnPostUpload(FileUpload fileUpload)
  2. {
  3. if (!Directory.Exists(fullPath))
  4. {
  5. Directory.CreateDirectory(fullPath);
  6. }
  7. foreach (var aformFile in fileUpload.FormFiles)
  8. {
  9. var formFile = aformFile;
  10. if (formFile.Length > 0)
  11. {
  12. var filePath = Path.Combine(fullPath, formFile.FileName);
  13. using (var stream = System.IO.File.Create(filePath))
  14. {
  15. formFile.CopyToAsync(stream);
  16. }
  17. }
  18. }
  19. // Process uploaded files
  20. // Don't rely on or trust the FileName property without validation.
  21. ViewData["SuccessMessage"] = fileUpload.FormFiles.Count.ToString() + " files uploaded!!";
  22. return Page();
  23. }
For multiple files
Complete Index.cshtml page
  1. @page
  2. @model IndexModel
  3. @{
  4. ViewData["Title"] = "Home page";
  5. }
  6. <div class="text-center">
  7. <h4 >Rijsat.com | Upload Single or Multiple files</h4>
  8. <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
  9. </div>
  10. <form enctype="multipart/form-data" method="post">
  11. <div class="form-group">
  12. <label class="file">
  13. <input type="file" asp-for="fileUpload.FormFiles" multiple aria-label="File browser example">
  14. <span class="file-custom"></span>
  15. </label>
  16. <input asp-page-handler="Upload" class="btn btn-primary" type="submit" value="Upload">
  17. </div>
  18. @{
  19. if (ViewData["SuccessMessage"] != null)
  20. {
  21. <span class="badge badge-success"> @ViewData["SuccessMessage"]</span>
  22. }
  23. }
  24. </form>
Complete Index.cshtml.cs code.
  1. using Microsoft.AspNetCore.Http;
  2. using Microsoft.AspNetCore.Mvc;
  3. using Microsoft.AspNetCore.Mvc.RazorPages;
  4. using Microsoft.Extensions.Logging;
  5. using System.Collections.Generic;
  6. using System.ComponentModel.DataAnnotations;
  7. using System.IO;
  8. namespace computervision.aspcore.Pages
  9. {
  10. public class IndexModel : PageModel
  11. {
  12. private readonly ILogger<IndexModel> _logger;
  13. private string fullPath = System.AppDomain.CurrentDomain.BaseDirectory.ToString() + "UploadImages";
  14. public IndexModel(ILogger<IndexModel> logger)
  15. {
  16. _logger = logger;
  17. }
  18. [BindProperty]
  19. public FileUpload fileUpload { get; set; }
  20. public void OnGet()
  21. {
  22. ViewData["SuccessMessage"] = "";
  23. }
  24. public IActionResult OnPostUpload(FileUpload fileUpload)
  25. {
  26. if (!Directory.Exists(fullPath))
  27. {
  28. Directory.CreateDirectory(fullPath);
  29. }
  30. foreach (var aformFile in fileUpload.FormFiles)
  31. {
  32. var formFile = aformFile;
  33. if (formFile.Length > 0)
  34. {
  35. var filePath = Path.Combine(fullPath, formFile.FileName);
  36. using (var stream = System.IO.File.Create(filePath))
  37. {
  38. formFile.CopyToAsync(stream);
  39. }
  40. }
  41. }
  42. // Process uploaded files
  43. // Don't rely on or trust the FileName property without validation.
  44. ViewData["SuccessMessage"] = fileUpload.FormFiles.Count.ToString() + " files uploaded!!";
  45. return Page();
  46. }
  47. }
  48. public class FileUpload
  49. {
  50. [Required]
  51. [Display(Name = "File")]
  52. public List<IFormFile> FormFiles { get; set; } // convert to list
  53. public string SuccessMessage { get; set; }
  54. }
  55. }
Now, again, we will run the solution and test it. This time, we will option to select multiple files and upload shown,
File Upload Insights And Upload Single Or Multiple Files In ASP.NET Core Razor Pages
We have completed single and multiple file upload successfully.

Storing files in physical storage and database

In the above solution, I have stored the files into physical location which is define as shown:
  1. private string fullPath = System.AppDomain.CurrentDomain.BaseDirectory.ToString() + "UploadImages";
Additionally, if we want to save the file into SQL server, we need to create a table to save file as shown:
  1. /****** Object: Table [dbo].[Document] Script Date: 4/26/2021 11:33:41 PM ******/
  2. SET ANSI_NULLS ON
  3. GO
  4. SET QUOTED_IDENTIFIER ON
  5. GO
  6. CREATE TABLE [dbo].[Document](
  7. [Id] [bigint] IDENTITY(1,1) NOT NULL,
  8. [FileName] [nvarchar](250) NOT NULL,
  9. [FileType] [varchar](100) NULL,
  10. [FileData] [varbinary](max) NOT NULL,
  11. [Created] [datetime] NOT NULL,
  12. [Modified] [datetime] NOT NULL,
  13. CONSTRAINT [PK_Document] PRIMARY KEY CLUSTERED
  14. (
  15. [Id] ASC
  16. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
  17. ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
  18. GO
Then create model based on the table, then generate value accordingly.
  1. public class DocumentViewmodel
  2. {
  3. public int Id { get; set; }
  4. [MaxLength(250)]
  5. public string FileName { get; set; }
  6. [MaxLength(100)]
  7. public string FileType { get; set; }
  8. [MaxLength]
  9. public byte[] FileData { get; set; }
  10. public DateTime Created { get; set; }
  11. public DateTime Modified { get; set; }
  12. }
Then you can get the model data according as shown,
  1. // File upload to database
  2. //Get File Name
  3. var filename = Path.GetFileName(formFile.FileName);
  4. //Get file Extension
  5. var fileextension = Path.GetExtension(fileName);
  6. // concatenating File Name and File Extension
  7. var newfilename = String.Concat(Convert.ToString(Guid.NewGuid()), fileextension);
  8. var documentViewmodel = new DocumentViewmodel() {
  9. Id = 0,
  10. FileName = newfilename,
  11. FileType = fileExtension,
  12. Created = DateTime.Now,
  13. Modified = DateTime.Now
  14. };
  15. using(var target = new MemoryStream()) {
  16. formFile.CopyTo(target);
  17. documentViewmodel.FileData = target.ToArray();
  18. }
  19. // use this documentViewmodel to save record in database

Conclusion

In this article, I have explained file uploading option which are generally two types: buffering and streaming. I have, furthermore, elaborated about security concerns on file upload as well listed some tips to reduce the attacks from file upload. Additionally, I have shared some file storage options available for file upload with comprehensive comparison. Finally, I have also created an asp.net core 5.0 with razor pages to demonstrate file upload for single and multiple files into physical location and database.