打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
ASP.NET Core MVC 从入门到精通之文件上传

随着技术的发展,ASP.NET Core MVC也推出了好长时间,经过不断的版本更新迭代,已经越来越完善,本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容,适用于初学者,在校毕业生,或其他想从事ASP.NET Core MVC 系统开发的人员。经过前几篇文章的讲解,初步了解ASP.NET Core MVC项目创建,启动运行,以及命名约定,创建控制器,视图,模型,接收参数,传递数据ViewData,ViewBag,路由,页面布局,wwwroot和客户端库,Razor语法,EnityFrameworkCore与数据库,HttpContext,Request,Response,Session,序列化等内容,今天继续讲解ASP.NET Core MVC 中文件上传等相关内容,仅供学习分享使用。

概述

在实际应用开发中,文件上传是非常常见的功能,文件上传主要分为单文件上传,多文件上传,文件与其他内容混合上传,大文件上传几种情况,本文会分别讲解。

IFormFile

在ASP.NET Core MVC项目中,IFormFile表示使用 HttpRequest 发送的文件,可用于文件流的接收。 是用于处理或保存文件的文件的 C# 表示形式。

文件上传使用的磁盘和内存取决于并发文件上传的数量和大小。如果应用尝试缓冲过多上传,站点就会在内存或磁盘空间不足时崩溃。如果文件上传的大小或频率会消耗应用资源,请使用流式传输。

IFormFile的属性和方法如下:

 对于小文件的上传,一般采用IFormFile;大文件上传,采用流式上传,以实现可靠稳定传输。

单个文件上传

单文件上传功能主要分为两部分:文件上传视图和后台处理方法。

1. 文件上传视图

首先创建视图,用于单个文件上传。关于视图有两点说明,如下所示:

  1. 文件上传通过form表单,采用post方式,加密类型为multipart/form-data

  2. 文件上传采用input控件,类型为file。

视图代码如下所示:

<form method="post" enctype="multipart/form-data" action="/File/OneFileUpload">    <h1>单文件上传</h1>    <div>        <span>文件:</span>        <input type="file" name="file" />    </div>    <input type="submit" value="上传" /></form>

2. 后台处理方法

form提交后台处理方法OneFileUpload,关于处理方法有几点说明,如下所示:

  1. 方法中的参数IFormFile  file用于接收客户端上传的文件,其他file和视图中上传控件的name一一对应。如果错误,则无法上传。

  2. _webHostEnvironment 为控制器通过接口注入的IWebHostEnvironment类型的获取站点信息接口,主要用于获取站点根目录。

  3. 调用IFormFile的CopyTo方法进行保存,此方法接收Stream类型的参数。

上传处理代码,如下所示:

/// <summary>/// 单文件上传/// </summary>/// <returns></returns>public IActionResult OneFileUpload(IFormFile file){    var path = Path.Combine(_webHostEnvironment.ContentRootPath, "uploads", string.Format("{0}_{1}", DateTime.Now.Ticks, file.FileName));    using (FileStream fs = new FileStream(path, FileMode.Create))    {        file.CopyTo(fs);    }    return Ok("上传成功");}
多文件上传

多文件上传表示一次可以上传多个文件。上传功能主要分为两部分:文件上传视图和后台处理方法

1. 多文件上传视图

input控件在类型为file时表示文件上传,默认是单个文件上传,通过设置multiple属性,可实现多文件上传。视图代码如下所示:

<form method="post" enctype="multipart/form-data" action="/File/MoreFileUpload">    <h1>多文件上传</h1>    <div>        <span>文件:</span>        <input type="file" name="files" multiple />    </div>    <input type="submit" value="上传" /></form>

2. 多文件后台处理方法

多个文件上传,参数为IFormFile数组类型,可以接收上传文件列表,然后循环获取并进行保存即可。如下所示:

/// <summary>/// 多文件上传/// </summary>/// <returns></returns>public IActionResult MoreFileUpload(IFormFile[] files){    foreach (var file in files)    {        var path = Path.Combine(_webHostEnvironment.ContentRootPath, "uploads", string.Format("{0}_{1}", DateTime.Now.Ticks, file.FileName));        using (FileStream fs = new FileStream(path, FileMode.Create))        {            file.CopyTo(fs);        }    }
return Ok("上传成功");}
文件文本混合上传

在实际应用中,文件上传只是一部分,还需要搭配其他的文本说明,如录入产品信息,并上传附件等。

1. 创建模型

在Product中,包含两个属性,一个字符串类型的Name,用于绑定名称,一个IFormFile类型的File,用于上传文件。如下所示:

namespace DemoCoreMVC.Models{    public class Product    {        public string Name { get; set; }
public IFormFile File { get; set; } }}

2. 视图绑定模型

在视图最顶部,为视图指定模型,如下所示:

@model DemoCoreMVC.Models.Product

3. 混合文本文件上传视图

在form表单中,除了文件上传控件,还有一个文件框,用于输入名称。其中控件name和模型相对应。如下所示:

<form method="post" enctype="multipart/form-data" action="/File/FileWithContentUpload">    <h1>文件,文本混合上传</h1>    <div>        <span>名称:</span>        <input type="text" name="Name"  />    </div>    <div>        <span>文件:</span>        <input type="file" name="File" />    </div>    <input type="submit" value="上传" /></form>

4. 后台处理方法

文件文本混合上传,参数为模型Product,通过属性匹配接收参数,然后获取属性File对应的内存流进行保存即可。如下所示:

/// <summary>/// 文件内容混合上传/// </summary>/// <returns></returns>public IActionResult FileWithContentUpload(Product product){    var file = product.File;    var path = Path.Combine(_webHostEnvironment.ContentRootPath, "uploads", string.Format("{0}_{1}", DateTime.Now.Ticks, file.FileName));    using (FileStream fs = new FileStream(path, FileMode.Create))    {        file.CopyTo(fs);    }    return Ok($"{product.Name} 上传成功");}
大文件上传

首先如何界定大文件/小文件,并没有统一的标准。根据官网相关参数说明:

  1. 默认情况下, HttpRequest.Form 不会缓冲整个请求正文 (BufferBody) ,但会缓冲包含的任何多部分表单文件。

  2. MultipartBodyLengthLimit 是缓冲表单文件的最大大小,默认值为 128MB。

  3. MemoryBufferThreshold 指示在转换为磁盘上的缓冲区文件之前,内存中的文件缓冲量,默认为 64KB。

  4. MemoryBufferThreshold 充当小型和大型文件之间的边界,这些文件根据应用资源和方案而引发或降低。

大文件上传采用流式传输,可降低上传文件时对内存或磁盘空间的需求。

1. 创建视图

大文件和小文件上传在视图上并无差别,只是后台处理方法不同,如下所示:

<form method="post" enctype="multipart/form-data" action="/File/BigFileUpload">    <h1>大文件上传</h1>    <div>        <span>文件:</span>        <input type="file" name="file" />    </div>    <input type="submit" value="上传" /></form>

2. 后台处理方法

首先创建大文件上传帮助类MultipartRequestHelper,如下所示:

using System;using System.IO;using Microsoft.Net.Http.Headers;namespace DemoCoreMVC{    public static class MultipartRequestHelper    {        // Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"        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 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)); }
// 如果一个section的Header是: Content-Disposition: form-data; name="myfile1"; filename="F:\Misc 002.jpg" // 那么本方法返回: Misc 002.jpg public static string GetFileName(ContentDispositionHeaderValue contentDisposition) { return Path.GetFileName(contentDisposition.FileName.Value); }
}}

处理方法BigFileUploadAsync,关于Action说明,如下所示:

Action中要进行DisableRequestSizeLimit特性说明,否则会有大小限制【InvalidDataException: Multipart body length limit 16384 exceeded】。

在该操作中,使用 MultipartReader 读取窗体的内容,它会读取每个单独的 MultipartSection,从而根据需要处理文件或存储内容。读取多部分节后,该操作会执行自己的模型绑定。

/// <summary>/// 大文件上传/// </summary>/// <returns></returns>[DisableRequestSizeLimit]public async Task<IActionResult> BigFileUploadAsync(){    var contentType = Request.ContentType;    if (!MultipartRequestHelper.IsMultipartContentType(contentType))    {        ModelState.AddModelError("File",            $"上传文件类型不对.");        return BadRequest(ModelState);    }    var path = Path.Combine(_webHostEnvironment.ContentRootPath, "uploads");
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)) { ModelState.AddModelError("File", $"The request couldn't be processed (Error 2).");
return BadRequest(ModelState); } else { var fileName = MultipartRequestHelper.GetFileName(contentDisposition); var loadBufferBytes = 1024;//这个是每一次从Http请求的section中读出文件数据的大小,单位是Byte即字节,这里设置为1024的意思是,每次从Http请求的section数据流中读取出1024字节的数据到服务器内存中,然后写入下面targetFileStream的文件流中,可以根据服务器的内存大小调整这个值。这样就避免了一次加载所有上传文件的数据到服务器内存中,导致服务器崩溃。
using (var targetFileStream = new FileStream(path + "\\" + string.Format("{0}_{1}", DateTime.Now.Ticks, fileName), FileMode.Create, FileAccess.ReadWrite)) { using (section.Body) { //section.Body是System.IO.Stream类型,表示的是Http请求中一个section的数据流,从该数据流中可以读出每一个section的全部数据,所以我们下面也可以不用section.Body.CopyToAsync方法,而是在一个循环中用section.Body.Read方法自己读出数据(如果section.Body.Read方法返回0,表示数据流已经到末尾,数据已经全部都读取完了),再将数据写入到targetFileStream await section.Body.CopyToAsync(targetFileStream, loadBufferBytes); } } } } section = await reader.ReadNextSectionAsync(); } return Ok("上传成功");}

注意:在文件上传功能中,上传后的文件一般都要进行重命名的,否则如果客户端上传相同名称的文件,则可能会被覆盖。在本例中,在原文件前面加上了时间戳,以减少重复的概率。

文件上传校验

在实际开发中,为了避免客户端上传不满足条件的文件,一般都会进行校验。

  1. 文件扩展名验证:应在允许的扩展名列表中查找上传的文件的扩展名。

  2. 文件签名验证:文件的签名由文件开头部分中的前几个字节确定。可以使用这些字节指示扩展名是否与文件内容匹配。示例应用检查一些常见文件类型的文件签名。

  3. 文件名安全:切勿使用客户端提供的文件名来将文件保存到物理存储。

  4. 文件大小验证:限制上传的文件的大小。

文件上传安全

为避免文件上传功能造成攻击可能性,常规安全措施如下:

  1. 将文件上传到专用文件上传区域,最好是非系统驱动器。使用专用位置便于对上传的文件实施安全限制。禁用对文件上传位置的执行权限。

  2. 请勿将上传的文件保存在与应用相同的目录树中。

  3. 使用应用确定的安全的文件名。请勿使用用户提供的文件名或上传的文件的不受信任的文件名。† 当显示不受信任的文件名时 HTML 会对它进行编码。例如,记录文件名或在 UI 中显示(Razor 自动对输出进行 HTML 编码)。

  4. 按照应用的设计规范,仅允许已批准的文件扩展名。

  5. 验证是否对服务器执行客户端检查。† 客户端检查易于规避。

  6. 检查已上传文件的大小。设置一个大小上限以防止上传大型文件。

  7. 文件不应该被具有相同名称的上传文件覆盖时,先在数据库或物理存储上检查文件名,然后再上传文件。

  8. 先对上传的内容运行病毒/恶意软件扫描程序,然后再存储文件。

参考文章

本篇文章主要参考内容如下:

1. 官方文档:https://learn.microsoft.com/zh-cn/aspnet/core/mvc/models/file-uploads?view=aspnetcore-6.0

以上就是ASP.NET Core MVC从入门到精通之文件上传的全部内容

关于ASP.NET Core MVC 从入门到精通其他文章,可通过以下链接查看:

ASP.NET Core MVC 从入门到精通之初窥门径

ASP.NET Core MVC 从入门到精通之接化发(一)

ASP.NET Core MVC 从入门到精通之接化发(二)

ASP.NET Core MVC 从入门到精通之路由

ASP.NET Core MVC 从入门到精通之布局

ASP.NET Core MVC 从入门到精通之wwwroot和客户端库

ASP.NET Core MVC 从入门到精通之Razor语法

ASP.NET Core MVC 从入门到精通之数据库

ASP.NET Core MVC 从入门到精通之HttpContext

ASP.NET Core MVC 从入门到精通之序列化


学习编程,从关注【老码识途】开始!!!

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
解决asp.net core 大文件上传问题。
ASP.NET MVC 文件上传教程(一)
Orchard架构介绍
ASP.NET MVC架构与实战系列之一:理解MVC底层运行机制
ASP.NET MVC3 Razor视图引擎
ASP.NET MVC4 入门之添加一个View
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服