学习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编程系 ...
随机推荐
- Python——索引与切片
#索引与切片 ##1.序列 序列:list,tuple,str 其中list是可变序列 typle,str是不可变序列 #修改序列的值 list = [3,4,5] tup = (3,4,5) str ...
- P3008 [USACO11JAN]Roads and Planes G (最短路+拓扑排序)
该最短路可不同于平时简单的最短路模板. 这道题一看就知道用SPFA,但是众所周知,USACO要卡spfa,所以要用更快的算法. 单向边不构成环,双向边都是非负的,所以可以将图分成若干个连通块(内部只有 ...
- for循环及range内置方法
目录 while循环补充说明 流程控制之for循环 range方法 rang实战案例 作业 """ 1.先写注释(思维逻辑和想法) 2.先考虑主体功能 在考虑附加功能 & ...
- MyBatis获取参数值的两种方式
MyBatis获取参数值的两种方式:${}和#{} ${}的本质就是字符串拼接,#{}的本质就是占位符赋值 ${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单 ...
- Annocation(注解)的使用示例
示例一:生成文档相关的注解示例二:在编译时进行格式检查(JDK内置的三个基本注解)@Override: 限定重写父类方法, 该注解只能用于方法@Deprecated: 用于表示所修饰的元素(类, 方法 ...
- day01-4-订座功能
满汉楼01-4 4.功能实现03 4.5订座功能 4.5.1功能说明 如果该餐桌处于已经预定或者就餐状态时,不能进行预定,并给出相应提示 4.5.2思路分析 根据显示界面,要考虑以下两种状态 检测餐桌 ...
- 线性表的基本操作(C语言实现)
文章目录 这里使用的工具是DEV C++ 可以借鉴一下 实现效果 顺序存储代码实现 链式存储存储实现 这里使用的工具是DEV C++ 可以借鉴一下 一.实训名称 线性表的基本操作 二.实训目的 1.掌 ...
- 2021年9月28日,老是遇到一些非常奇葩的问题。就离谱、好好的一个web项目就莫名奇妙坏了。
起因是这样的:我前几天用idea2020编辑器,用ssm框架搭建了一个图书管理系统.只是将图书信息的增删改查实现,还有用户的注册和登录功能实现.本来想着今天将用户信息的删除和修改完善以下,本来是很简单 ...
- Educational Codeforces Round 122 (Rated for Div. 2)/codeforces1633
CodeForces1633 Div. 7 解析: 题目大意 给定 \(t\) 组数据.每组数据给定一个数 \(n\)(\(10\le n\le 999\)). 每次操作可以修改 \(n\) 任意一位 ...
- VS使用正则表达式删除程序中的空行
Ctrl+H; 需要替换的正则表达式 ^(?([^\r\n])\s)*\r?$\r?\n