上传文件 FileUpload
ASP.NET Core 支持使用缓冲的模型绑定(针对较小文件)和无缓冲的流式传输(针对较大文件)上传一个或多个文件。
文件上传方案
缓冲和流式传输是上传文件的两种常见方法。
缓冲
如果文件上传的大小或频率会消耗应用资源,请使用流式传输。
流式处理
流式传输无法显著提高性能。 流式传输可降低上传文件时对内存或磁盘空间的需求。
通过缓冲的模型绑定将小型文件上传到物理存储
上传到服务器的单个文件可使用 IFormFile 接口通过模型绑定进行访问。
使用模型绑定和 IFormFile 上传文件时,操作方法可以接受以下内容:
- 单个 IFormFile。
- 以下任何表示多个文件的集合:
appsetting.json 配置参数
"StoredFilesPath": "Images", "FileSizeLimit": 2097152
using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Protocols; using MvcMovie.Data; using MvcMovie.Models; using MvcMovie.ViewModels; using static System.Net.Mime.MediaTypeNames; namespace MvcMovie.Controllers; public class FileUploadController : Controller { IWebHostEnvironment _host; IConfiguration _config; MvcMovieContext _dbContext; string _storedFilesPath = string.Empty;//上传文件目录 readonly long _fileSizeLimit;//文件大小限制 public FileUploadController(IWebHostEnvironmenthost, IConfiguration config, MvcMovieContext dbContext) { _host = host; _config = config; _storedFilesPath = Path.Combine(_host.WebRootPath, _config["StoredFilesPath"]); _fileSizeLimit = _config.GetValue<long>("FileSizeLimit"); _dbContext = dbContext; } public IActionResult Index() { return View(); } //上传单个文件 [HttpPost] public async Task<IActionResult> Upload(FileUpload fileUpload) { long size = fileUpload.FormFile.Length; if (size > 0) { // var filePath = Path.Combine(_storedFilesPath, //Path.GetRandomFileName()); var filePath = Path.Combine(_storedFilesPath, GenerateFileName(fileUpload.FormFile.FileName)); using (var stream = System.IO.File.Create(filePath)) { await fileUpload.FormFile.CopyToAsync(stream); } } return RedirectToAction("Index"); } public IActionResult UploadMultiple() { return View(); } //上传多个文件 [HttpPost] public async Task<IActionResult> UploadMultiple(FileUpload fileUpload) { long size = fileUpload.FormFiles.Sum(f => f.Length); foreach (var formFile in fileUpload.FormFiles) { if (formFile.Length > 0) { var filePath = Path.Combine(_storedFilesPath, Path.GetRandomFileName()); 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. var count = (new { count = fileUpload.FormFiles.Count, size }); return RedirectToAction("Index"); } public IActionResult UploadDb() { return View(); } //上传文件到数据库中的byte[]字段 [HttpPost] public async Task<IActionResult> UploadDb(BufferedSingleFileUploadDbViewModel viewModel) { if (viewModel == null || viewModel.FormFile == null) { ModelState.AddModelError("", "请选择一个文件。"); return View(); } if (!ValidFileExtension(viewModel.FormFile.FileName)) { ModelState.AddModelError("", "不支持的文件类型。"); return View(); } using (var memoryStrema = new MemoryStream()) { await viewModel.FormFile.CopyToAsync(memoryStrema); //仅能上传文件小于2MB if (memoryStrema.Length < _fileSizeLimit) { var file = new AppFile { Content = memoryStrema.ToArray() }; _dbContext.appFiles.Add(file); await _dbContext.SaveChangesAsync(); } else { ModelState.AddModelError("", "上传文件大不能超过2M。"); return View(); } } return RedirectToAction("Index"); } /// <summary> /// 文件扩展名验证 /// </summary> /// <param name="uploadedFileName">上传文件名</param> /// <returns></returns> private bool ValidFileExtension(string uploadedFileName) { string[] permittedExtensions = { ".txt", ".pdf", ".doc" }; var ext = Path.GetExtension(uploadedFileName).ToLowerInvariant(); if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext)) { return false; } return true; } /// <summary> /// 生成文件名 /// </summary> /// <param name="uploadedFileName"></param> /// <returns></returns> private string GenerateFileName(string uploadedFileName) { string fileName = Guid.NewGuid().ToString(); fileName = $"{fileName}{Path.GetExtension(uploadedFileName)}"; return fileName; } }
public class FileUpload { public IFormFile FormFile { get; set; } public List<IFormFile> FormFiles { get; set;} }
一、上传单个文件
@model FileUpload <h3>上传单个文件</h3> <form enctype="multipart/form-data" asp-action="Upload" method="post"> <dl> <dt> <label asp-for="FormFile"></label> </dt> <dd> <input asp-for="FormFile" type="file" /> <span asp-validation-for="FormFile"></span> </dd> </dl> <input class="btn btn-primary" type="submit" value="Upload" /> </form> <a asp-action="UploadMultiple">上传多个文件</a> <a asp-action="UploadDb">上传文件到数据库</a>
二、上传多个文件
@model FileUpload <h2>一次选择多个文件上传</h2> <form enctype="multipart/form-data" asp-action="UploadMultiple" method="post"> <dl> <dt> <label asp-for="FormFiles"></label> </dt> <dd> <input asp-for="FormFiles" type="file" multiple /> <span asp-validation-for="FormFiles"></span> </dd> </dl> <input class="btn btn-primary" type="submit" value="Upload" /> </form>
三、上传文件到数据库
public class AppFile { public int Id { get; set; } public byte[] Content { get; set; } }
public class BufferedSingleFileUploadDbViewModel { [Required] [Display(Name = "文件")] public IFormFile FormFile { get; set; } }
@model BufferedSingleFileUploadDbViewModel <h3>上传文件到数据库</h3> <form enctype="multipart/form-data" asp-action="UploadDb" method="post"> <dl> <dt> <label asp-for="FormFile"></label> </dt> <dd> <input asp-for="FormFile" type="file" /> <span asp-validation-for="FormFile"></span> </dd> </dl> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <input class="btn btn-primary" type="submit" value="Upload" /> </form>
服务器和应用程序配置
多部分正文长度限制
MultipartBodyLengthLimit 设置每个多部分正文的长度限制。 分析超出此限制的窗体部分时,会引发 InvalidDataException。 默认值为 134,217,728 (128 MB)。 使用 Startup.ConfigureServices
中的 MultipartBodyLengthLimit 设置自定义此限制:
public void ConfigureServices(IServiceCollection services) { services.Configure<FormOptions>(options => { // Set the limit to 256 MB options.MultipartBodyLengthLimit = 268435456; }); }
在 Razor Pages 应用或 MVC 应用中,将筛选器应用到页面模型或操作方法:
// Set the limit to 256 MB [RequestFormLimits(MultipartBodyLengthLimit = 268435456)] public class BufferedSingleFileUploadPhysicalModel : PageModel { ... }
参考:https://learn.microsoft.com/zh-cn/aspnet/core/mvc/models/file-uploads?view=aspnetcore-7.0