ASP.NET Core supports uploading one or more files using buffered model binding for smaller files and unbuffered streaming for larger files.
// ----------------------------------------------------------------------------
// AppFile.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Models/AppFile.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Models/AppFile.cs
// ----------------------------------------------------------------------------
using System;
using System.ComponentModel.DataAnnotations;
namespace SampleApp.Models
{
public class AppFile
{
public int Id { get; set; }
public byte[] Content { get; set; }
[Display(Name = "File Name")]
public string UntrustedName { get; set; }
[Display(Name = "Note")]
public string Note { get; set; }
[Display(Name = "Size (bytes)")]
[DisplayFormat(DataFormatString = "{0:N0}")]
public long Size { get; set; }
[Display(Name = "Uploaded (UTC)")]
[DisplayFormat(DataFormatString = "{0:G}")]
public DateTime UploadDT { get; set; }
}
}
// ----------------------------------------------------------------------------
// SampleAppContext.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Data/SampleAppContext.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Data/SampleAppContext.cs
// ----------------------------------------------------------------------------
using Microsoft.EntityFrameworkCore;
using SampleApp.Models;
namespace SampleApp.Data
{
public class AppDbContext : DbContext
{
public AppDbContext (DbContextOptions<AppDbContext> options)
: base(options)
{
}
public DbSet<AppFile> File { get; set; }
}
}
// ----------------------------------------------------------------------------
// FileHelpers.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Utilities/FileHelpers.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Utilities/FileHelpers.cs
// ----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;
namespace SampleApp.Utilities
{
public static class FileHelpers
{
// If you require a check on specific characters in the IsValidFileExtensionAndSignature
// method, supply the characters in the _allowedChars field.
private static readonly byte[] _allowedChars = { };
// For more file signatures, see the File Signatures Database (https://www.filesignatures.net/)
// and the official specifications for the file types you wish to add.
private static readonly Dictionary<string, List<byte[]>> _fileSignature = new Dictionary<string, List<byte[]>>
{
{ ".gif", new List<byte[]> { new byte[] { 0x47, 0x49, 0x46, 0x38 } } },
{ ".png", new List<byte[]> { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } } },
{ ".jpeg", new List<byte[]>
{
new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 },
new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 },
}
},
{ ".jpg", new List<byte[]>
{
new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 },
new byte[] { 0xFF, 0xD8, 0xFF, 0xE8 },
}
},
{ ".zip", new List<byte[]>
{
new byte[] { 0x50, 0x4B, 0x03, 0x04 },
new byte[] { 0x50, 0x4B, 0x4C, 0x49, 0x54, 0x45 },
new byte[] { 0x50, 0x4B, 0x53, 0x70, 0x58 },
new byte[] { 0x50, 0x4B, 0x05, 0x06 },
new byte[] { 0x50, 0x4B, 0x07, 0x08 },
new byte[] { 0x57, 0x69, 0x6E, 0x5A, 0x69, 0x70 },
}
},
};
// **WARNING!**
// In the following file processing methods, the file's content isn't scanned.
// In most production scenarios, an anti-virus/anti-malware scanner API is
// used on the file before making the file available to users or other
// systems. For more information, see the topic that accompanies this sample
// app.
public static async Task<byte[]> ProcessFormFile<T>(IFormFile formFile,
ModelStateDictionary modelState, string[] permittedExtensions,
long sizeLimit)
{
var fieldDisplayName = string.Empty;
// Use reflection to obtain the display name for the model
// property associated with this IFormFile. If a display
// name isn't found, error messages simply won't show
// a display name.
MemberInfo property = typeof(T).GetProperty(
formFile.Name.Substring(formFile.Name.IndexOf(".",
StringComparison.Ordinal) + 1));
if (property != null)
{
if (property.GetCustomAttribute(typeof(DisplayAttribute)) is DisplayAttribute displayAttribute)
{
fieldDisplayName = $"{displayAttribute.Name} ";
}
}
// Don't trust the file name sent by the client. To display
// the file name, HTML-encode the value.
var trustedFileNameForDisplay = WebUtility.HtmlEncode(formFile.FileName);
// Check the file length. This check doesn't catch files that only have
// a BOM as their content.
if (formFile.Length == 0)
{
modelState.AddModelError(formFile.Name,
$"{fieldDisplayName}({trustedFileNameForDisplay}) is empty.");
return new byte[0];
}
if (formFile.Length > sizeLimit)
{
var megabyteSizeLimit = sizeLimit / 1048576;
modelState.AddModelError(formFile.Name,
$"{fieldDisplayName}({trustedFileNameForDisplay}) exceeds " +
$"{megabyteSizeLimit:N1} MB.");
return new byte[0];
}
try
{
using (var memoryStream = new MemoryStream())
{
await formFile.CopyToAsync(memoryStream);
// Check the content length in case the file's only
// content was a BOM and the content is actually
// empty after removing the BOM.
if (memoryStream.Length == 0)
{
modelState.AddModelError(formFile.Name,
$"{fieldDisplayName}({trustedFileNameForDisplay}) is empty.");
}
if (!IsValidFileExtensionAndSignature(
formFile.FileName, memoryStream, permittedExtensions))
{
modelState.AddModelError(formFile.Name,
$"{fieldDisplayName}({trustedFileNameForDisplay}) file " +
"type isn't permitted or the file's signature " +
"doesn't match the file's extension.");
}
else
{
return memoryStream.ToArray();
}
}
}
catch (Exception ex)
{
modelState.AddModelError(formFile.Name,
$"{fieldDisplayName}({trustedFileNameForDisplay}) upload failed. " +
$"Please contact the Help Desk for support. Error: {ex.HResult}");
// Log the exception
}
return new byte[0];
}
public static async Task<byte[]> ProcessStreamedFile(
MultipartSection section, ContentDispositionHeaderValue contentDisposition,
ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit)
{
try
{
using (var memoryStream = new MemoryStream())
{
await section.Body.CopyToAsync(memoryStream);
// 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();
}
}
}
catch (Exception ex)
{
modelState.AddModelError("File",
"The upload failed. Please contact the Help Desk " +
$" for support. Error: {ex.HResult}");
// Log the exception
}
return new byte[0];
}
private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, string[] permittedExtensions)
{
if (string.IsNullOrEmpty(fileName) || data == null || data.Length == 0)
{
return false;
}
var ext = Path.GetExtension(fileName).ToLowerInvariant();
if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext))
{
return false;
}
data.Position = 0;
using (var reader = new BinaryReader(data))
{
if (ext.Equals(".txt") || ext.Equals(".csv") || ext.Equals(".prn"))
{
if (_allowedChars.Length == 0)
{
// Limits characters to ASCII encoding.
for (var i = 0; i < data.Length; i++)
{
if (reader.ReadByte() > sbyte.MaxValue)
{
return false;
}
}
}
else
{
// Limits characters to ASCII encoding and
// values of the _allowedChars array.
for (var i = 0; i < data.Length; i++)
{
var b = reader.ReadByte();
if (b > sbyte.MaxValue || !_allowedChars.Contains(b))
{
return false;
}
}
}
return true;
}
// Uncomment the following code block if you must permit
// files whose signature isn't provided in the _fileSignature
// dictionary. We recommend that you add file signatures
// 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
// --------------------
// With the file signatures provided in the _fileSignature
// dictionary, the following code tests the input content's
// file signature.
var signatures = _fileSignature[ext];
var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length));
return signatures.Any(signature => headerBytes.Take(signature.Length).SequenceEqual(signature));
}
}
}
}
// ----------------------------------------------------------------------------
// MultipartRequestHelper.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Utilities/MultipartRequestHelper.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Utilities/MultipartRequestHelper.cs
// ----------------------------------------------------------------------------
using System;
using System.IO;
using Microsoft.Net.Http.Headers;
namespace SampleApp.Utilities
{
public static class MultipartRequestHelper
{
// Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"
// The spec at https://tools.ietf.org/html/rfc2046#section-5.1 states that 70 characters is a reasonable limit.
public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
{
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value;
if (string.IsNullOrWhiteSpace(boundary))
{
throw new InvalidDataException("Missing content-type boundary.");
}
if (boundary.Length > lengthLimit)
{
throw new InvalidDataException(
$"Multipart boundary length limit {lengthLimit} exceeded.");
}
return boundary;
}
public static bool IsMultipartContentType(string contentType)
{
return !string.IsNullOrEmpty(contentType)
&& contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0;
}
public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition)
{
// Content-Disposition: form-data; name="key";
return contentDisposition != null
&& contentDisposition.DispositionType.Equals("form-data")
&& string.IsNullOrEmpty(contentDisposition.FileName.Value)
&& string.IsNullOrEmpty(contentDisposition.FileNameStar.Value);
}
public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition)
{
// Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg"
return contentDisposition != null
&& contentDisposition.DispositionType.Equals("form-data")
&& (!string.IsNullOrEmpty(contentDisposition.FileName.Value)
|| !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value));
}
}
}
// ----------------------------------------------------------------------------
// Antiforgery.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Filters/Antiforgery.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Filters/Antiforgery.cs
// ----------------------------------------------------------------------------
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
namespace SampleApp.Filters
{
public class GenerateAntiforgeryTokenCookieAttribute : ResultFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
var antiforgery = context.HttpContext.RequestServices.GetService<IAntiforgery>();
// Send the request token as a JavaScript-readable cookie
var tokens = antiforgery.GetAndStoreTokens(context.HttpContext);
context.HttpContext.Response.Cookies.Append(
"RequestVerificationToken",
tokens.RequestToken,
new CookieOptions() { HttpOnly = false });
}
public override void OnResultExecuted(ResultExecutedContext context)
{
}
}
}
// ----------------------------------------------------------------------------
// ModelBinding.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Filters/ModelBinding.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Filters/ModelBinding.cs
// ----------------------------------------------------------------------------
using System;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace SampleApp.Filters
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
var factories = context.ValueProviderFactories;
factories.RemoveType<FormValueProviderFactory>();
factories.RemoveType<FormFileValueProviderFactory>();
factories.RemoveType<JQueryFormValueProviderFactory>();
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
}
// ----------------------------------------------------------------------------
// StreamingController.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Controllers/StreamingController.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Controllers/StreamingController.cs
// ----------------------------------------------------------------------------
using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using SampleApp.Data;
using SampleApp.Filters;
using SampleApp.Models;
using SampleApp.Utilities;
namespace SampleApp.Controllers
{
public class StreamingController : Controller
{
private readonly AppDbContext _context;
private readonly long _fileSizeLimit;
private readonly ILogger<StreamingController> _logger;
private readonly string[] _permittedExtensions = { ".txt" };
private readonly string _targetFilePath;
// Get the default form options so that we can use them to set the default
// limits for request body data.
private static readonly FormOptions _defaultFormOptions = new FormOptions();
public StreamingController(ILogger<StreamingController> logger,
AppDbContext context, IConfiguration config)
{
_logger = logger;
_context = context;
_fileSizeLimit = config.GetValue<long>("FileSizeLimit");
// To save physical files to a path provided by configuration:
_targetFilePath = config.GetValue<string>("StoredFilesPath");
// To save physical files to the temporary files folder, use:
//_targetFilePath = Path.GetTempPath();
}
// The following upload methods:
//
// 1. Disable the form value model binding to take control of handling
// potentially large files.
//
// 2. Typically, antiforgery tokens are sent in request body. Since we
// don't want to read the request body early, the tokens are sent via
// headers. The antiforgery token filter first looks for tokens in
// the request header and then falls back to reading the body.
#region snippet_UploadDatabase
[HttpPost]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UploadDatabase()
{
if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
{
ModelState.AddModelError("File",
$"The request couldn't be processed (Error 1).");
// Log error
return BadRequest(ModelState);
}
// Accumulate the form data key-value pairs in the request (formAccumulator).
var formAccumulator = new KeyValueAccumulator();
var trustedFileNameForDisplay = string.Empty;
var untrustedFileNameForStorage = string.Empty;
var streamedFileContent = new byte[0];
var boundary = MultipartRequestHelper.GetBoundary(
MediaTypeHeaderValue.Parse(Request.ContentType),
_defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
var section = await reader.ReadNextSectionAsync();
while (section != null)
{
var hasContentDispositionHeader =
ContentDispositionHeaderValue.TryParse(
section.ContentDisposition, out var contentDisposition);
if (hasContentDispositionHeader)
{
if (MultipartRequestHelper
.HasFileContentDisposition(contentDisposition))
{
untrustedFileNameForStorage = contentDisposition.FileName.Value;
// Don't trust the file name sent by the client. To display
// the file name, HTML-encode the value.
trustedFileNameForDisplay = WebUtility.HtmlEncode(
contentDisposition.FileName.Value);
streamedFileContent =
await FileHelpers.ProcessStreamedFile(section, contentDisposition,
ModelState, _permittedExtensions, _fileSizeLimit);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
}
else if (MultipartRequestHelper
.HasFormDataContentDisposition(contentDisposition))
{
// Don't limit the key name length because the
// multipart headers length limit is already in effect.
var key = HeaderUtilities
.RemoveQuotes(contentDisposition.Name).Value;
var encoding = GetEncoding(section);
if (encoding == null)
{
ModelState.AddModelError("File",
$"The request couldn't be processed (Error 2).");
// Log error
return BadRequest(ModelState);
}
using (var streamReader = new StreamReader(
section.Body,
encoding,
detectEncodingFromByteOrderMarks: true,
bufferSize: 1024,
leaveOpen: true))
{
// The value length limit is enforced by
// MultipartBodyLengthLimit
var value = await streamReader.ReadToEndAsync();
if (string.Equals(value, "undefined",
StringComparison.OrdinalIgnoreCase))
{
value = string.Empty;
}
formAccumulator.Append(key, value);
if (formAccumulator.ValueCount >
_defaultFormOptions.ValueCountLimit)
{
// Form key count limit of
// _defaultFormOptions.ValueCountLimit
// is exceeded.
ModelState.AddModelError("File",
$"The request couldn't be processed (Error 3).");
// Log error
return BadRequest(ModelState);
}
}
}
}
// Drain any remaining section body that hasn't been consumed and
// read the headers for the next section.
section = await reader.ReadNextSectionAsync();
}
// Bind form data to the model
var formData = new FormData();
var formValueProvider = new FormValueProvider(
BindingSource.Form,
new FormCollection(formAccumulator.GetResults()),
CultureInfo.CurrentCulture);
var bindingSuccessful = await TryUpdateModelAsync(formData, prefix: "",
valueProvider: formValueProvider);
if (!bindingSuccessful)
{
ModelState.AddModelError("File",
"The request couldn't be processed (Error 5).");
// Log error
return BadRequest(ModelState);
}
// **WARNING!**
// In the following example, the file is saved without
// scanning the file's contents. In most production
// scenarios, an anti-virus/anti-malware scanner API
// is used on the file before making the file available
// for download or for use by other systems.
// For more information, see the topic that accompanies
// this sample app.
var file = new AppFile()
{
Content = streamedFileContent,
UntrustedName = untrustedFileNameForStorage,
Note = formData.Note,
Size = streamedFileContent.Length,
UploadDT = DateTime.UtcNow
};
_context.File.Add(file);
await _context.SaveChangesAsync();
return Created(nameof(StreamingController), null);
}
#endregion
#region snippet_UploadPhysical
[HttpPost]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UploadPhysical()
{
if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
{
ModelState.AddModelError("File", $"The request couldn't be processed (Error 1).");
// Log error
return BadRequest(ModelState);
}
var boundary = MultipartRequestHelper.GetBoundary(
MediaTypeHeaderValue.Parse(Request.ContentType),
_defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
var section = await reader.ReadNextSectionAsync();
while (section != null)
{
var hasContentDispositionHeader =
ContentDispositionHeaderValue.TryParse(
section.ContentDisposition, out var contentDisposition);
if (hasContentDispositionHeader)
{
// This check assumes that there's a file
// present without form data. If form data
// is present, this method immediately fails
// and returns the model error.
if (!MultipartRequestHelper
.HasFileContentDisposition(contentDisposition))
{
ModelState.AddModelError("File", $"The request couldn't be processed (Error 2).");
// Log error
return BadRequest(ModelState);
}
else
{
// Don't trust the file name sent by the client. To display
// the file name, HTML-encode the value.
var trustedFileNameForDisplay = WebUtility.HtmlEncode(
contentDisposition.FileName.Value);
var trustedFileNameForFileStorage = Path.GetRandomFileName();
// **WARNING!**
// In the following example, the file is saved without
// scanning the file's contents. In most production
// scenarios, an anti-virus/anti-malware scanner API
// is used on the file before making the file available
// for download or for use by other systems.
// 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);
_logger.LogInformation(
"Uploaded file '{TrustedFileNameForDisplay}' saved to " +
"'{TargetFilePath}' as {TrustedFileNameForFileStorage}",
trustedFileNameForDisplay, _targetFilePath,
trustedFileNameForFileStorage);
}
}
}
// Drain any remaining section body that hasn't been consumed and
// read the headers for the next section.
section = await reader.ReadNextSectionAsync();
}
return Created(nameof(StreamingController), null);
}
#endregion
private static Encoding GetEncoding(MultipartSection section)
{
var hasMediaTypeHeader =
MediaTypeHeaderValue.TryParse(section.ContentType, out var mediaType);
// UTF-7 is insecure and shouldn't be honored. UTF-8 succeeds in
// most cases.
if (!hasMediaTypeHeader || Encoding.UTF7.Equals(mediaType.Encoding))
{
return Encoding.UTF8;
}
return mediaType.Encoding;
}
}
public class FormData
{
public string Note { get; set; }
}
}
// ----------------------------------------------------------------------------
// BufferedMultipleFileUploadPhysicalModel.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/BufferedMultipleFileUploadPhysical.cshtml.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/BufferedMultipleFileUploadPhysical.cshtml.cs
// ----------------------------------------------------------------------------
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration;
using SampleApp.Utilities;
namespace SampleApp.Pages
{
public class BufferedMultipleFileUploadPhysicalModel : PageModel
{
private readonly long _fileSizeLimit;
private readonly string[] _permittedExtensions = { ".txt" };
private readonly string _targetFilePath;
public BufferedMultipleFileUploadPhysicalModel(IConfiguration config)
{
_fileSizeLimit = config.GetValue<long>("FileSizeLimit");
// To save physical files to a path provided by configuration:
_targetFilePath = config.GetValue<string>("StoredFilesPath");
// To save physical files to the temporary files folder, use:
//_targetFilePath = Path.GetTempPath();
}
[BindProperty]
public BufferedMultipleFileUploadPhysical FileUpload { get; set; }
public string Result { get; private set; }
public void OnGet()
{
}
public async Task<IActionResult> OnPostUploadAsync()
{
if (!ModelState.IsValid)
{
Result = "Please correct the form.";
return Page();
}
foreach (var formFile in FileUpload.FormFiles)
{
var formFileContent =
await FileHelpers
.ProcessFormFile<BufferedMultipleFileUploadPhysical>(
formFile, ModelState, _permittedExtensions,
_fileSizeLimit);
if (!ModelState.IsValid)
{
Result = "Please correct the form.";
return Page();
}
// For the file name of the uploaded file stored
// server-side, use Path.GetRandomFileName to generate a safe
// random file name.
var trustedFileNameForFileStorage = Path.GetRandomFileName();
var filePath = Path.Combine(_targetFilePath, trustedFileNameForFileStorage);
// **WARNING!**
// In the following example, the file is saved without
// scanning the file's contents. In most production
// scenarios, an anti-virus/anti-malware scanner API
// is used on the file before making the file available
// for download or for use by other systems.
// For more information, see the topic that accompanies
// this sample.
using (var fileStream = System.IO.File.Create(filePath))
{
await fileStream.WriteAsync(formFileContent);
// To work directly with the FormFiles, use the following
// instead:
//await formFile.CopyToAsync(fileStream);
}
}
return RedirectToPage("./Index");
}
}
public class BufferedMultipleFileUploadPhysical
{
[Required]
[Display(Name="File")]
public List<IFormFile> FormFiles { get; set; }
[Display(Name="Note")]
[StringLength(50, MinimumLength = 0)]
public string Note { get; set; }
}
}
// ----------------------------------------------------------------------------
// BufferedMultipleFileUploadPhysical.cshtml
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/BufferedMultipleFileUploadPhysical.cshtml
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/BufferedMultipleFileUploadPhysical.cshtml
// ----------------------------------------------------------------------------
@page
@model BufferedMultipleFileUploadPhysicalModel
@{
ViewData["Title"] = "Buffered Multiple File Upload (Physical)";
}
<h1>Upload multiple buffered files to physical storage with one file upload control</h1>
<p>One or more files can be selected by the visitor. The following form's page handler saves the file(s) to disk.</p>
<form enctype="multipart/form-data" method="post">
<dl>
<dt>
<label asp-for="FileUpload.FormFiles"></label>
</dt>
<dd>
<input asp-for="FileUpload.FormFiles" type="file" multiple />
<span asp-validation-for="FileUpload.FormFiles"></span>
</dd>
</dl>
<input asp-page-handler="Upload" class="btn" type="submit" value="Upload" />
</form>
<p class="result">
@Model.Result
</p>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
// ----------------------------------------------------------------------------
// BufferedSingleFileUploadPhysical.cshtml.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/BufferedSingleFileUploadPhysical.cshtml.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/BufferedSingleFileUploadPhysical.cshtml.cs
// ----------------------------------------------------------------------------
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration;
using SampleApp.Utilities;
namespace SampleApp.Pages
{
public class BufferedSingleFileUploadPhysicalModel : PageModel
{
private readonly long _fileSizeLimit;
private readonly string[] _permittedExtensions = { ".txt" };
private readonly string _targetFilePath;
public BufferedSingleFileUploadPhysicalModel(IConfiguration config)
{
_fileSizeLimit = config.GetValue<long>("FileSizeLimit");
// To save physical files to a path provided by configuration:
_targetFilePath = config.GetValue<string>("StoredFilesPath");
// To save physical files to the temporary files folder, use:
//_targetFilePath = Path.GetTempPath();
}
[BindProperty]
public BufferedSingleFileUploadPhysical FileUpload { get; set; }
public string Result { get; private set; }
public void OnGet()
{
}
public async Task<IActionResult> OnPostUploadAsync()
{
if (!ModelState.IsValid)
{
Result = "Please correct the form.";
return Page();
}
var formFileContent =
await FileHelpers.ProcessFormFile<BufferedSingleFileUploadPhysical>(
FileUpload.FormFile, ModelState, _permittedExtensions,
_fileSizeLimit);
if (!ModelState.IsValid)
{
Result = "Please correct the form.";
return Page();
}
// For the file name of the uploaded file stored
// server-side, use Path.GetRandomFileName to generate a safe
// random file name.
var trustedFileNameForFileStorage = Path.GetRandomFileName();
var filePath = Path.Combine(_targetFilePath, trustedFileNameForFileStorage);
// **WARNING!**
// In the following example, the file is saved without
// scanning the file's contents. In most production
// scenarios, an anti-virus/anti-malware scanner API
// is used on the file before making the file available
// for download or for use by other systems.
// For more information, see the topic that accompanies
// this sample.
using (var fileStream = System.IO.File.Create(filePath))
{
await fileStream.WriteAsync(formFileContent);
// To work directly with a FormFile, use the following instead:
//await FileUpload.FormFile.CopyToAsync(fileStream);
}
return RedirectToPage("./Index");
}
}
public class BufferedSingleFileUploadPhysical
{
[Required]
[Display(Name="File")]
public IFormFile FormFile { get; set; }
[Display(Name="Note")]
[StringLength(50, MinimumLength = 0)]
public string Note { get; set; }
}
}
// ----------------------------------------------------------------------------
// BufferedSingleFileUploadPhysical.cshtml
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/BufferedSingleFileUploadPhysical.cshtml
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/BufferedSingleFileUploadPhysical.cshtml
// ----------------------------------------------------------------------------
@page
@model BufferedSingleFileUploadPhysicalModel
@{
ViewData["Title"] = "Buffered Single File Upload (Physical)";
}
<h1>Upload one buffered file to physical storage with one file upload control</h1>
<p>The following form's page handler saves the file to disk.</p>
<form enctype="multipart/form-data" method="post">
<dl>
<dt>
<label asp-for="FileUpload.FormFile"></label>
</dt>
<dd>
<input asp-for="FileUpload.FormFile" type="file" />
<span asp-validation-for="FileUpload.FormFile"></span>
</dd>
</dl>
<input asp-page-handler="Upload" class="btn" type="submit" value="Upload" />
</form>
<p class="result">
@Model.Result
</p>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
// ----------------------------------------------------------------------------
// _ValidationScriptsPartial.cshtml
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/Shared/_ValidationScriptsPartial.cshtml
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/Shared/_ValidationScriptsPartial.cshtml
// ----------------------------------------------------------------------------
<environment include="Development">
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</environment>
<environment exclude="Development">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"
asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator"
crossorigin="anonymous"
integrity="sha256-F6h55Qw6sweK+t7SiOJX+2bpSAa3b/fnlrVCJvmEj1A=">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"
asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
crossorigin="anonymous"
integrity="sha256-9GycpJnliUjJDVDqP0UEu/bsm9U+3dnQUH8+3W10vkY=">
</script>
</environment>
// ----------------------------------------------------------------------------
// _Layout.cshtml
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/Shared/_Layout.cshtml
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/Shared/_Layout.cshtml
// ----------------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewData["Title"]</title>
<style>body{margin:0;padding-bottom:20px;font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff;}h1{font-size:24px;margin:.67em 0;}pre{overflow:auto;}code,pre{font-family:monospace, monospace;font-size:1em;}input[type="text"]{width:50%;}button,input{margin:0;font:inherit;color:inherit;}button{overflow:visible;}button{text-transform:none;}:before,*:after{box-sizing:border-box;}a{color:#337ab7;text-decoration:none;}h1,h2,h3,h4{font-family:inherit;font-weight:500;line-height:1.1;color:inherit;margin-top:20px;margin-bottom:10px;}h3{font-size:16px;}h4{font-size:18px;}p{margin:0 0 10px;}ol{margin-top:0;margin-bottom:10px;}code,pre{font-family:Menlo, Monaco, Consolas, "Courier New", monospace;}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px;}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;-radius:4px;}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0;}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto;}.container{width:800px;}.btn{display:inline-block;padding:6px 12px;margin:10px 5px;font-size:14px;font-weight:normal;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;background-image:none;border:1px solid transparent;border-radius:4px;;color:#000;background-color:#fff;border-color:gray;}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0, 0, 0, .05);}.panel-body{padding:15px;}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px;}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit;}.panel-default{border-color:#ddd;}.panel-default > .panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd;}.clearfix:before,.clearfix:after,.container:before,.container:after,.panel-body:before,.panel-body:after{display:table;content:" ";}.clearfix:after,.container:after,.panel-body:after{clear:both;}.body-content{padding-left:15px;padding-right:15px;}.panel-body{font-size:16px;}table{border-collapse:collapse;width:100%;}th,td{padding:8px;border-bottom:1px solid #ddd;}th{text-align:center;}dt{font-weight:bold;text-decoration:underline;}dd{margin:0;padding:0 0 0.5em 0;}.text-center{text-align:center}.field-validation-error{color:red;display:block}.result{color:red}</style>
</head>
<body>
<nav>
<div class="container">
<ul>
<li><a asp-page="/Index">Home</a></li>
<li>
Database upload examples
<ul>
<li><a asp-page="/BufferedSingleFileUploadDb">Upload one buffered file with one file upload control</a></li>
<li><a asp-page="/BufferedMultipleFileUploadDb">Upload multiple buffered files with one file upload control</a></li>
<li><a asp-page="/BufferedDoubleFileUploadDb">Upload two buffered files with two file upload controls</a></li>
<li><a asp-page="/StreamedSingleFileUploadDb">Stream a file with AJAX to a controller endpoint</a></li>
</ul>
</li>
<li>
Physical storage upload examples
<ul>
<li><a asp-page="/BufferedSingleFileUploadPhysical">Upload one buffered file with one file upload control</a></li>
<li><a asp-page="/BufferedMultipleFileUploadPhysical">Upload multiple buffered files with one file upload control</a></li>
<li><a asp-page="/BufferedDoubleFileUploadPhysical">Upload two buffered files with two file upload controls</a></li>
<li><a asp-page="/StreamedSingleFileUploadPhysical">Stream a file with AJAX to a controller endpoint</a></li>
</ul>
</li>
</ul>
</div>
</nav>
<div class="container body-content">
@RenderBody()
</div>
<environment include="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
</environment>
<environment exclude="Development">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery"
crossorigin="anonymous"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
</script>
</environment>
@RenderSection("Scripts", required: false)
</body>
</html>
// ----------------------------------------------------------------------------
// DeletePhysicalFile.cshtml.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/DeletePhysicalFile.cshtml.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/DeletePhysicalFile.cshtml.cs
// ----------------------------------------------------------------------------
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.FileProviders;
namespace SampleApp.Pages
{
public class DeletePhysicalFileModel : PageModel
{
private readonly IFileProvider _fileProvider;
public DeletePhysicalFileModel(IFileProvider fileProvider)
{
_fileProvider = fileProvider;
}
public IFileInfo RemoveFile { get; private set; }
public IActionResult OnGet(string fileName)
{
if (string.IsNullOrEmpty(fileName))
{
return RedirectToPage("/Index");
}
RemoveFile = _fileProvider.GetFileInfo(fileName);
if (!RemoveFile.Exists)
{
return RedirectToPage("/Index");
}
return Page();
}
public IActionResult OnPost(string fileName)
{
if (string.IsNullOrEmpty(fileName))
{
return RedirectToPage("/Index");
}
RemoveFile = _fileProvider.GetFileInfo(fileName);
if (RemoveFile.Exists)
{
System.IO.File.Delete(RemoveFile.PhysicalPath);
}
return RedirectToPage("./Index");
}
}
}
// ----------------------------------------------------------------------------
// StreamedSingleFileUploadPhysical.cshtml.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/StreamedSingleFileUploadPhysical.cshtml.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/StreamedSingleFileUploadPhysical.cshtml.cs
// ----------------------------------------------------------------------------
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace SampleApp.Pages
{
public class StreamedSingleFileUploadPhysicalModel : PageModel
{
public void OnGet()
{
}
}
}
// ----------------------------------------------------------------------------
// StreamedSingleFileUploadPhysical.cshtml
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/StreamedSingleFileUploadPhysical.cshtml
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/StreamedSingleFileUploadPhysical.cshtml
// ----------------------------------------------------------------------------
@page
@model StreamedSingleFileUploadPhysicalModel
@{
ViewData["Title"] = "Streamed Single File Upload with AJAX (Physical)";
}
<h1>Stream a file with AJAX to physical storage with a controller endpoint</h1>
<p>
The following form's <code>action</code> points to a controller endpoint that only receives the file and saves it to disk.
If additional form data is added to the form, the additional data is ignored by the controller action.
</p>
<form id="uploadForm" action="Streaming/UploadPhysical" method="post"
enctype="multipart/form-data" onsubmit="AJAXSubmit(this);return false;">
<dl>
<dt>
<label for="file">File</label>
</dt>
<dd>
<input id="file" type="file" name="file" />
</dd>
</dl>
<input class="btn" type="submit" value="Upload" />
<div style="margin-top:15px">
<output form="uploadForm" name="result"></output>
</div>
</form>
@section Scripts {
<script>
"use strict";
async function AJAXSubmit (oFormElement) {
const formData = new FormData(oFormElement);
try {
const response = await fetch(oFormElement.action, {
method: 'POST',
headers: {
'RequestVerificationToken': getCookie('RequestVerificationToken')
},
body: formData
});
oFormElement.elements.namedItem("result").value =
'Result: ' + response.status + ' ' + response.statusText;
} catch (error) {
console.error('Error:', error);
}
}
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2) return parts.pop().split(";").shift();
}
</script>
}
// ----------------------------------------------------------------------------
// Index.cshtml.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/Index.cshtml.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/Index.cshtml.cs
// ----------------------------------------------------------------------------
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Mime;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.FileProviders;
using SampleApp.Data;
using SampleApp.Models;
namespace SampleApp.Pages
{
public class IndexModel : PageModel
{
private readonly AppDbContext _context;
private readonly IFileProvider _fileProvider;
public IndexModel(AppDbContext context, IFileProvider fileProvider)
{
_context = context;
_fileProvider = fileProvider;
}
public IList<AppFile> DatabaseFiles { get; private set; }
public IDirectoryContents PhysicalFiles { get; private set; }
public async Task OnGetAsync()
{
DatabaseFiles = await _context.File.AsNoTracking().ToListAsync();
PhysicalFiles = _fileProvider.GetDirectoryContents(string.Empty);
}
public async Task<IActionResult> OnGetDownloadDbAsync(int? id)
{
if (id == null)
{
return Page();
}
var requestFile = await _context.File.SingleOrDefaultAsync(m => m.Id == id);
if (requestFile == null)
{
return Page();
}
// Don't display the untrusted file name in the UI. HTML-encode the value.
return File(requestFile.Content, MediaTypeNames.Application.Octet, WebUtility.HtmlEncode(requestFile.UntrustedName));
}
public IActionResult OnGetDownloadPhysical(string fileName)
{
var downloadFile = _fileProvider.GetFileInfo(fileName);
return PhysicalFile(downloadFile.PhysicalPath, MediaTypeNames.Application.Octet, fileName);
}
}
}
// ----------------------------------------------------------------------------
// Index.cshtml
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/Index.cshtml
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Pages/Index.cshtml
// ----------------------------------------------------------------------------
@page
@model IndexModel
@{
ViewData["Title"] = "Upload Files Sample";
}
<h1>Files in the database</h1>
@if (Model.DatabaseFiles.Count == 0)
{
<p>
No files are available. Visit one of the file upload scenario pages to upload one or more files.
</p>
}
else
{
<table>
<thead>
<tr>
<th></th>
<th>
@Html.DisplayNameFor(model => model.DatabaseFiles[0].UntrustedName) /
@Html.DisplayNameFor(model => model.DatabaseFiles[0].Note)
</th>
<th>
@Html.DisplayNameFor(model => model.DatabaseFiles[0].UploadDT)
</th>
<th>
@Html.DisplayNameFor(model => model.DatabaseFiles[0].Size)
</th>
<th>
<code>FileStreamResult</code> from database
</th>
</tr>
</thead>
<tbody>
@foreach (var file in Model.DatabaseFiles) {
<tr>
<td>
<a asp-page="./DeleteDbFile" asp-route-id="@file.Id">Delete</a>
</td>
<td>
<b>@file.UntrustedName</b><br>
@Html.DisplayFor(modelItem => file.Note)
</td>
<td class="text-center">
@Html.DisplayFor(modelItem => file.UploadDT)
</td>
<td class="text-center">
@Html.DisplayFor(modelItem => file.Size)
</td>
<td class="text-center">
<a asp-page-handler="DownloadDb" asp-route-id="@file.Id">Download</a>
</td>
</tr>
}
</tbody>
</table>
}
<h1>Files stored on disk</h1>
@if (Model.PhysicalFiles.Count() == 0)
{
<p>
No files are available. Visit one of the file upload scenario pages to upload one or more files.
</p>
}
else
{
<table>
<thead>
<tr>
<th></th>
<th>
Name / Path
</th>
<th>
Size (bytes)
</th>
<th>
<code>PhysicalFileResult</code> from storage
</th>
</tr>
</thead>
<tbody>
@foreach (var file in Model.PhysicalFiles) {
<tr>
<td>
<a asp-page="./DeletePhysicalFile" asp-route-fileName="@file.Name">Delete</a>
</td>
<td>
<b>@file.Name</b><br>
@file.PhysicalPath
</td>
<td class="text-center">
@file.Length.ToString("N0")
</td>
<td class="text-center">
<a asp-page-handler="DownloadPhysical" asp-route-fileName="@file.Name">Download</a>
</td>
</tr>
}
</tbody>
</table>
}
// ----------------------------------------------------------------------------
// Startup.cs
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Startup.cs
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/Startup.cs
// ----------------------------------------------------------------------------
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using SampleApp.Data;
using SampleApp.Filters;
namespace SampleApp
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
#region snippet_AddRazorPages
services.AddRazorPages()
.AddRazorPagesOptions(options =>
{
options.Conventions
.AddPageApplicationModelConvention("/StreamedSingleFileUploadDb",
model =>
{
model.Filters.Add(new GenerateAntiforgeryTokenCookieAttribute());
model.Filters.Add(new DisableFormValueModelBindingAttribute());
});
options.Conventions
.AddPageApplicationModelConvention("/StreamedSingleFileUploadPhysical",
model =>
{
model.Filters.Add(new GenerateAntiforgeryTokenCookieAttribute());
model.Filters.Add(new DisableFormValueModelBindingAttribute());
});
});
#endregion
// To list physical files from a path provided by configuration:
var physicalProvider = new PhysicalFileProvider(Configuration.GetValue<string>("StoredFilesPath"));
// To list physical files in the temporary files folder, use:
//var physicalProvider = new PhysicalFileProvider(Path.GetTempPath());
services.AddSingleton<IFileProvider>(physicalProvider);
services.AddDbContext<AppDbContext>(options => options.UseInMemoryDatabase("InMemoryDb"));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints => {
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
}
}
// ----------------------------------------------------------------------------
// appsettings.json
//
// Article: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1
// Path: AspNetCore.Docs/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/appsettings.json
// Source: https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/appsettings.json
// ----------------------------------------------------------------------------
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"StoredFilesPath": "c:\\files",
"FileSizeLimit": 2097152
}