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

news2024/11/24 0:31:43

随着技术的发展,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 发送的文件,可以用于文件流的接收。将整个文件读入 IFormFile。 IFormFile 是用于处理或保存文件的文件的 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从入门到精通之文件上传的全部内容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/508603.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VMware NSX-T Data Center 3.2.2.1 - 数据中心网络全栈虚拟化

请访问原文链接&#xff1a;https://sysin.org/blog/vmware-nsx-t-3/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org VMware NSX-T Data Center 3.2.2.1 | 30 MAR 2023 | Build 21487560 VMware NSX-T Data Center 3.2.2 | 08 …

NOA上车「清一色」自主品牌,哪些供应商正在突围前线

随着入门级L2进入普及周期&#xff0c;以NOA&#xff08;高速、城区&#xff09;为代表的L2/L2赛道&#xff0c;正在成为主机厂、硬件供应商、算法及软件方案商的下一波市场制高点的争夺阵地。 高工智能汽车研究院监测数据显示&#xff0c;2023年1-3月中国市场&#xff08;不含…

MySQL基础(十六)变量、流程控制与游标

1. 变量 在MySQL数据库的存储过程和函数中&#xff0c;可以使用变量来存储查询或计算的中间结果数据&#xff0c;或者输出最终的结果数据。 在 MySQL 数据库中&#xff0c;变量分为系统变量以及用户自定义变量。 1.1 系统变量 1.1.1 系统变量分类 变量由系统定义&#xff…

【Nacos在derby模式下密码忘记】使用derby的ij工具重置密码/修改密码

【问题描述】 nacos部署未用mysql,直接运行&#xff0c;使用了默认的derby数据库&#xff0c;这时候不一小心修改的密码给忘记了&#xff0c;无法登录 当时是部署在centos上的一个演示环境&#xff0c;没有采用mysql数据库&#xff0c;如果生产上&#xff0c;建议使用mysql。 …

php用户分享信息技术交流大学生论坛系统vue

系统应实现的目标 1. 提供安全、友好的操作环境&#xff1a;避免一些网上的不良言论&#xff0c;创造一个和谐的网络环境。 2. 提供发表帖子功能&#xff1a;注册的用户可以自由发帖&#xff0c;发表符合法律法规的言论。 3. 提供回复帖子功能&#xff1a;户可以自由回复&am…

camunda执行监听器如何使用

在Camunda工作流引擎中&#xff0c;执行监听器是一种机制&#xff0c;用于在业务流程执行期间捕获特定事件并执行相应的操作。它们可以帮助您实现一些重要的任务&#xff0c;例如&#xff1a; 1、记录或更新业务数据&#xff1a;当流程中的任务或事件发生时&#xff0c;您可以…

工程监测无线中继采集发送仪 指示灯功能说明及接口定义

工程监测NLM5无线中继采集发送仪 指示灯功能说明及接口定义 指示灯功能说明 标识 名称 状态 描述说明 备注说明 CHG 正在充电 常亮 正在充电 DON 充电完成 常亮 已充满 POW 电源指示 常亮 外部电源已连接 仅用于指示是否连接了外部电源 熄灭 无外部电源 SIG 空 RUN 运行状态 闪…

“数字社区”诞生 “智慧大脑”助力提升社区综合治理效能

近日&#xff0c;民政局等9部门印发的《关于深入推进智慧社区建设的意见》提出&#xff0c;到2025年&#xff0c;基本构建起网格化管理、精细化服务、信息化支撑、开放共享的智慧社区服务平台&#xff0c;初步打造成智慧共享、和睦共治的新型数字社区。 数字社区建设是智慧城市…

最优化理论-线性规划解的几何特征

目录 一、引言 二、线性规划的定义 三、线性规划的几何特征 1.可行域 2.最优解 3.等价约束 4.对偶问题 四、线性规划的应用 五、结论 一、引言 最优化理论是数学中的一个重要分支&#xff0c;它研究如何在给定的约束条件下&#xff0c;寻找一个最优解。其中&#xff…

【MySql】数据库索引

数据库索引 索引索引的创建索引的查看索引的删除 聚簇索引 & 非聚簇索引聚簇索引非聚簇索引 索引创建原则 索引 可以简单理解为一本书的目录信息&#xff0c;是为了提升查找效率而建立的 索引的创建 1、在创建一个主键、唯一键、外键时候&#xff0c;数据库会自动地针对查…

Express框架的安装和使用

1.Express框架简介 Node.js的web框架发展至今,第一个知名的框架为Connect框架.它类似一个中间件的脚手架.只提供逻辑,不实现具体的处理逻辑.中间件概念的引入Express框架奠定了基础. 2.Express框架的安装 安装分为局部安装和全局安装. 2.1局部安装 1.在D盘创建expressStud…

html实现开心消消乐小游戏

文章目录 1.设计来源1.1 游戏界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/130594511 html实现开心消消乐小游戏源码 《开心消消乐》 是一款三消游戏&#xff0c;游…

站群服务器和普通服务器区别

更有利于提升 站群服务器指的是对于站群系统提升客户开发设计的网络服务器&#xff0c;客户租服务器来置放好几个网站&#xff0c;许多客户以便免费在线上扩大曝出会挑选提升好几个网站。非站群服务器&#xff0c;基础只有置放两三个网站&#xff0c;并且在管理方法时也…

跨模态检索论文泛读:VisualSparta-利用加权的词袋进行大规模的文本到图像的检索

ACL2021 | 利用加权的词袋进行大规模的文本到图像的检索 VisualSparta: An Embarrassingly Simple Approach to Large-scale Text-to-Image Search with Weighted Bag-of-words主打速度&#xff01; 简介 目前的跨模态检索方法主要分为查询相关和查询无关两种。查询无关的方法…

js实现产品页点击小图在大图区显示

企业网站产品图片可能会比较多&#xff0c;需要在产品页面多放几张展示图片&#xff0c;我们可以使用一张大图几张小图的形式排列&#xff0c;并使用js代码实现点击小图显示大图。效果如下所示 html代码部分&#xff1a; <div class"img_bd"> <img src"…

Windows在外远程桌面控制macOS 【macOS自带VNC远程】

文章目录 前言1.测试局域网内远程控制1.1 macOS打开屏幕共享1.2 测试局域网内VNC远程控制 2. 测试公网远程控制2.1 macOS安装配置cpolar内网穿透2.2 创建tcp隧道&#xff0c;指向5900端口 3. 测试公网远程控制4. 配置公网固定TCP地址4.1 保留固定TCP地址4.2 配置固定TCP端口地址…

在树莓派上搭建WordPress博客网站【内网穿透】

文章目录 概述安装 PHP安装MySQL数据库安装 Wordpress设置您的 WordPress 数据库设置 MySQL/MariaDB创建 WordPress 数据库 WordPress configuration将WordPress站点发布到公网安装相对URL插件修改config.php配置 支持好友链接样式定制主题 转载自cpolar极点云的文章&#xff1…

k8s集群部署 | 二进制三节点(复用)高可用集群部署过程

文章目录 1. 二进制部署三节点&#xff08;复用&#xff09;高可用 k8s 集群1.1 环境规划阶段1.1.1 实验架构图1.1.2 系统版本说明1.1.3 环境基本信息1.1.4 k8s 网段划分 1.2 基础安装及优化阶段1.2.1 系统信息检查1.2.2 静态 IP 地址配置1.2.3 配置主机名1.2.4 配置/etc/hosts…

基于绝缘状态的煤矿电缆绝缘可视化在线检测系统

摘要&#xff1a;针对供电系统绝缘问题检测技术限制煤炭产量效率的问题&#xff0c;以某煤炭企业6kV井下供电系统为研究对象&#xff0c;开展了在线监测系统设计与应用工作。结果表明&#xff0c;系统工作稳定&#xff0c;满足井下电力电缆绝缘在线监要求&#xff0c;降低了井下…

Pycharm运行unittest报错ModuleNotFoundError: No module named ‘pytest‘解决

使用unittest未import pytest相关功能语句&#xff0c;在pycharm中右键run的时候报错&#xff1a; Traceback (most recent call last):File "B:\Application\pycharm\PyCharm 2023.1\plugins\python\helpers\pycharm\_jb_pytest_runner.py", line 5, in <modul…