学习ASP.NET Core Blazor编程系列十七——文件上传(上)
从本篇文章开始我们来讲在图书租赁系统中如何使用内置的文件上传组件进行文件上传功能的开发。本文的示例适合上传小型文件。本篇文章演示如何通过Blazor的内置组件InputFile将文件上传至服务器。
安全注意事项
在向用户提供向上传文件的功能时,必须格外注意安全性。攻击者可能对系统执行拒绝服务和其他攻击。所以在提供上传功能时需要注意以下安全措施:
1. 将文件上传到系统上的专用文件上传目录,这样可以更轻松地对上传内容实施安全措施。如果允许文件上传,请确保在上传目录禁用执行权限。
2. 上传文件的文件名在服务器端保存时要由应用程序自动重新命名文件名称,而不是采用用户输入或已上传文件的文件名。
3.请不要将上传的文件保存在与应用程序相同的目录下。
4. 仅允许使用一组特定的文件扩展名。
5. 在服务端重新执行客户端检查。 不要相信客户端检查,因为客户端检查很容易规避。
6. 检查上传文件大小,防止上传文件的大小比预期的文件大小大。
7. 对上传文件的内容进行病毒/恶意软件扫描程序。
8.应用程序中的文件不能被具有相同名称的上传文件覆盖。
警告
将恶意代码上传到系统通常是执行代码的第一步,这些代码可以实现以下功能:
1. 完全接管系统。
2. 重载系统,导致系统完全崩溃。
3. 泄露用户或系统数据。
一、添加一个用于上传文件的文件辅助类FileHelpers
为避免处理上传文件文件时出现重复代码,我们首先创建一个静态类用于处理上传功能。
1.在Visual Studio 2022 的解决方案资源管理器中创建一个“Utils”文件夹。
2.在Visual Studio 2022的解决方案资源管理器中,鼠标左键选中“Utils”文件夹,右键单击,在弹出菜单中选择“添加—>类”(如下图)。 将类命名为“FileHelpers”。

3.在Visual Studio 2022的文本编辑器中打开我们刚才创建的“FileHelpers.cs”类文件,并添加以下内容。其中方法 ProcessFormFile 接受 IBrowserFile 和 ModelStateDictionary等参数,保存成功则空字符串,否则返回错误信息。 检查内容类型和长度。 如果上传文件未通过校验,将向 ModelState 添加一个错误。
using BlazorAppDemo.Models;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Reflection;
using System.Text;
namespace BlazorAppDemo.Utils
{ public class FileHelpers
{ public static async Task<string> ProcessFormFile(IBrowserFile formFile, ModelStateDictionary modelState,IWebHostEnvironment envir,int maxFileSize)
{
var fieldDisplayName = string.Empty;
if (!string.IsNullOrEmpty(formFile.Name))
{
// 如果名称没有找到,将会有一个简单的错误消息,但不会显示文件名称
string displayFileName = formFile.Name.Substring(formFile.Name.IndexOf(".") + 1); fieldDisplayName = $"{displayFileName} ";
}
//使用path.GetFileName获取一个带路径的全文件名。
//通过HtmlEncode进行编码的结果必须在错误消息中返回。
var fileName = WebUtility.HtmlEncode(Path.GetFileName(formFile.Name));
if (formFile.ContentType.ToLower() != "text/plain")
{
modelState.AddModelError(formFile.Name,
$"The {fieldDisplayName}file ({fileName}) must be a text file.");
}
//校验文件长度,如果文件不包含内容,则不必读取文件长度。
//此校验不会检查仅具有BOM(字节顺序标记)作为内容的文件,
//因此在读取文件内容后再次检验文件内容长度,以校验仅包含BOM的文件。
if (formFile.Size == 0)
{
modelState.AddModelError(formFile.Name, $"The {fieldDisplayName}file ({fileName}) is empty."); }
else if (formFile.Size > maxFileSize)
{ modelState.AddModelError(formFile.Name, $"The {fieldDisplayName}file ({fileName}) exceeds 1 MB.");
}
else
{
try
{
//获取一个随机文件名
var trustedFileNameForFileStorage=Path.GetRandomFileName();
var path = Path.Combine(envir.ContentRootPath, envir.EnvironmentName, "unsafeUploads", trustedFileNameForFileStorage);
using (
var reader =
new FileStream(
path,
FileMode.Create))
{
await formFile.OpenReadStream(maxFileSize).CopyToAsync(reader);
} }
catch (Exception ex)
{
modelState.AddModelError(formFile.Name, $"The {fieldDisplayName}file ({fileName}) upload failed. " + $"Please contact the Help Desk for support. Error: {ex.Message}");
//return ex.Message;
throw ex;
}
}
return string.Empty;
} } }
1. 在Visual Studio 2022的解决方案资源管理器中,鼠标右键单击“Pages”文件夹。在弹出菜单中选择,添加-->Razor组件。如下图。

2.在弹出对话框,名称中输入FileUpload1.razor。如下图。

3. 在Visual Studio 2022的解决方案资源管理器中,鼠标左键双击“Pages\FileUpload1.razor”文件,在文本编辑器中打开,在文件的顶部添加@page指令。并添加如下代码。
@page "/FileUpload1"
@using BlazorAppDemo.Utils
@using Microsoft.AspNetCore.Mvc.ModelBinding
@inject IWebHostEnvironment Environ
<h3>多文件上传示例</h3>
<p>
<label>
提示信息:@Message
</label>
</p>
<p>
<label>
上传文件最大可以为:<input type="number" @bind="maxFileSize"/>字节
</label>
</p>
<p>
<label>
一次可上传:<input type="number" @bind="maxAllowedFiles" />个文件
</label>
</p>
<p>
<label>
选择上传文件:<InputFile OnChange="@LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>文件上传中......</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>文件名:@file.Name</li>
<li>最后修改时间:@file.LastModified.ToString()</li>
<li>文件大小(byte):@file.Size</li>
<li>文件类型:@file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 18;
private int maxAllowedFiles = 2;
private bool isLoading;
private string Message = string.Empty;
private async Task LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
ModelStateDictionary modelState = new ModelStateDictionary();
loadedFiles.Add(file);
string result= await FileHelpers.ProcessFormFile(file, modelState, Environ, maxFileSize); if (string.IsNullOrEmpty(result))
{
Message = "上传成功!";
}else
Message = "上传失败!";
}
catch (Exception ex)
{
Message = ex.Message;
}
}
isLoading = false;
}
}
4. 在Visual Studio 2022的解决方案资源管理器中,鼠标左键双击“Shared\NavMenu.razor”文件,在文本编辑器中打开,我们在此文中添加指向上传文件的菜单。具体代码如下:
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">BlazorAppDemo</a> <button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="BookIndex">
<span class="oi oi-list-rich" aria-hidden="true"></span> 图书列表
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="AddBook">
<span class="oi oi-list-rich" aria-hidden="true"></span> 添加图书
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="FileUpload1">
<span class="oi oi-list-rich" aria-hidden="true"></span> 上传文件
</NavLink>
</div>
</nav>
</div>
@code {
private bool collapseNavMenu = true; private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; private void ToggleNavMenu()
{ collapseNavMenu = !collapseNavMenu; } }
5. 在Visual Studio 2022的菜单栏上,找到“调试à开始调试”或是按F5键,Visual Studio 2022会生成BlazorAppDemo应用程序,并在浏览器中打开Home页面,我们使用鼠标点击左边的菜单栏上的“上传文件”菜单项,页面会进入“FileUpload1”页面,我们会看到我们写的图书列表页面,如下图。

6. 我们在“多文件上传示例”中选择一个上传文件,然后应用程序会自动上传文件,但是却会提示错误,错误信息如下图中1处,指明“找不到路径的一部分”。我们打开资源管理器,在项目中找一下图中2处的目录,发现没有这样的目录结构。我们手动创建一下即可。 
7. 我们在“多文件上传示例”中选择一个上传文件,然后应用程序会自动上传文件,上传到到目录中却不是我们选择的文件名,是一个随机的文件名。如下图。

学习ASP.NET Core Blazor编程系列十七——文件上传(上)的更多相关文章
- 学习ASP.NET Core Blazor编程系列六——新增图书(上)
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...
- 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(上)
学习ASP.NET Core Blazor编程系列一--综述 一.概述 Blazor 是一个生成交互式客户端 Web UI 的框架: 使用 C# 代替 JavaScript 来创建信息丰富的交互式 U ...
- 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(中)
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 四.创建一个Blazor应用程序 1. 第一种创 ...
- 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(下)
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...
- 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(完)
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...
- 022年9月12日 学习ASP.NET Core Blazor编程系列三——实体
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...
- 学习ASP.NET Core Blazor编程系列四——迁移
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...
- 学习ASP.NET Core Blazor编程系列五——列表页面
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...
- 学习ASP.NET Core Blazor编程系列六——初始化数据
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...
- 学习ASP.NET Core Blazor编程系列八——数据校验
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...
随机推荐
- java基础-冒泡排序以及稀疏数组
java基础 以下内容为本人的学习笔记,如需要转载,请声明原文链接 https://www.cnblogs.com/lyh1024/p/16720908.html Ø 冒泡排序原理: 比较数组中, ...
- influxDB2.2
下载安装 下载地址 下载后在解压目录中,输入cmd执行exe文件 浏览器访问localhost:8086 选择快速开始,填写用户信息,组织信息 相关概念 InfluxDB是一个由InfluxData开 ...
- PTA 乙级解题笔记 1001 害死人不偿命的(3n+1)猜想
卡拉兹(Callatz)猜想: 对任何一个正整数 n,如果它是偶数,那么把它砍掉一半:如果它是奇数,那么把 (3n+1) 砍掉一半.这样一直反复砍下去,最后一定在某一步得到 n=1.卡拉兹在 1950 ...
- C#-12 转换
一 什么是转换 转换是接受一个类型的值并使用它作为另一个类型的等价值的过程. 下列代码演示了将1个short类型的值强制转换成byte类型的值. short var1 = 5; byte var2 = ...
- 【算法】浅学 LCA
参考资料 浅析最近公共祖先(LCA) 最近公共祖先 - OI Wiki [白话系列]倍增算法 一.概念 最近公共祖先称为 LCA (Lowest Common Ancestor) 它指的是在一颗树中, ...
- 中国制霸生成器「GitHub 热点速览 v.22.42」
火遍推特的中国制霸生成器本周一开源就占据了两天的 GitHub Trending 榜,不知道你的足迹遍布了多少个省份呢?同样记录痕迹的 kanal 用了内存读写方式解决了 Rust 的消息处理问题,P ...
- flutter 系列之:flutter 中的幽灵offstage
目录 简介 Offstage详解 Offstage的使用 总结 简介 我们在使用flutter的过程中,有时候需要控制某些组件是否展示,一种方法是将这个组件从render tree中删除,这样这个组件 ...
- 我的Spark学习笔记
一.架构设计 Driver根据用户代码构建计算流图,拆解出分布式任务并分发到 Executors 中去:每个Executors收到任务,然后处理这个 RDD 的一个数据分片子集 DAGSchedule ...
- 「浙江理工大学ACM入队200题系列」问题 B: 零基础学C/C++12——求平均值
本题是浙江理工大学ACM入队200题第二套中的B题 我们先来看一下这题的题面. 由于是比较靠前的题目,这里插一句.各位新ACMer朋友们,请一定要养成仔细耐心看题的习惯,尤其是要利用好输入和输出样例. ...
- 当前数据库表空间达到32G,需要扩容
表空间名:cwy_init 操作:给cwy_init增加数据文件,分配5G的空间,达到瓶颈自动增长1G,如下: alter tablespace cwy_init add datafile '/u01 ...