学习ASP.NET Core Razor 编程系列十三——文件上传功能(一)
学习ASP.NET Core Razor 编程系列目录
学习ASP.NET Core Razor 编程系列二——添加一个实体
学习ASP.NET Core Razor 编程系列三——创建数据表及创建项目基本页面
学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面
学习ASP.NET Core Razor 编程系列五——Asp.Net Core Razor新建模板页面
学习ASP.NET Core Razor 编程系列六——数据库初始化
学习ASP.NET Core Razor 编程系列七——修改列表页面
学习ASP.NET Core Razor 编程系列八——并发处理
学习ASP.NET Core Razor 编程系列九——增加查询功能
学习ASP.NET Core Razor 编程系列十——添加新字段
学习ASP.NET Core Razor 编程系列十一——把新字段更新到数据库
学习ASP.NET Core Razor 编程系列十二——在页面中增加校验
本篇文章我们来讲在书籍信息管理系统示例使用简单的模型绑定上传文件,本文的示例适合上传小型文件。本篇文章演示如何通过单个 POST 将两个文件上传至服务器。
安全注意事项
在向用户提供向上传文件的功能时,必须格外注意安全性。 攻击者可能对系统执行拒绝服务和其他攻击。 所以在提供上传功能时需要注意以下安全措施:
1. 将文件上传到系统上的专用文件上传目录,这样可以更轻松地对上传内容实施安全措施。如果允许文件上传,请确保在上传目录禁用执行权限。
2. 上传文件的文件名在服务器端保存时要由应用程序自动重新命名文件名称,而不是采用用户输入或已上传文件的文件名。
3. 仅允许使用一组特定的文件扩展名。
4. 在服务端重新执行客户端检查。 不要相信客户端检查,因为客户端检查很容易规避。
5. 检查上传文件大小,防止上传文件的大小比预期的文件大小大。
6. 对上传文件的内容进行病毒/恶意软件扫描程序。
警告
将恶意代码上传到系统通常是执行代码的第一步,这些代码可以实现以下功能:
1. 完全接管系统。
2. 重载系统,导致系统完全崩溃。
3. 泄露用户或系统数据。
一、添加类FileUpload
在Visual Studio 2017的解决方案资源管理器中,鼠标左键选中“Models”文件夹,右键单击,在弹出菜单中选择“添加—>类”(如下图)。 将类命名为“FileUpload”,并添加以下代码,代码如下:

using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks; namespace RazorMvcBooks.Models
{
public class FileUpload
{ [Required]
[Display(Name = "文件名")]
[StringLength(, MinimumLength = )]
public string FileName { get; set; } [Required]
[Display(Name = "公共描述")]
public IFormFile UploadPublicDescribe { get; set; } [Required]
[Display(Name = "后台描述")]
public IFormFile UploadPrivateDescribe { get; set; }
} }
此类有一个属性对应文件名称,另各有一个属性对应相应的上传文件。 3 个属性皆为必需属性,文件名长度必须为 3-60 个字符。 FileUpload 类与页面绑定以获取上传文件数据。
二、添加一个用于上传文件的文件辅助类FileHelpers
为避免处理上传文件文件时出现重复代码,我们首先创建一个静态文件用于处理上传功能。
在Visual Studio 2017 的解决方案资源管理器中创建一个“Utils”文件夹,然后此文件夹下创建一个“FileHelpers.cs”类文件,并添加以下内容。方法 ProcessFormFile 接受 IFormFile 和 ModelStateDictionary,并返回包含文件大小和内容的字符串。 检查内容类型和长度。 如果上传文件未通过校验,将向 ModelState 添加一个错误。
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using RazorMvcBooks.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks; namespace RazorMvcBooks.Utils
{
public class FileHelpers
{
public static async Task<string> ProcessFormFile(IFormFile formFile, ModelStateDictionary modelState)
{
var fieldDisplayName = string.Empty; // 使用反射获得的IFormFile实例对象的文件名称。
// 如果名称没有找到,将会有一个简单的错误消息,但不会显示文件名称
MemberInfo property =
typeof(FileUpload).GetProperty(formFile.Name.Substring(formFile.Name.IndexOf(".") + )); if (property != null)
{
var displayAttribute =
property.GetCustomAttribute(typeof(DisplayAttribute)) as DisplayAttribute; if (displayAttribute != null)
{
fieldDisplayName = $"{displayAttribute.Name} ";
}
} //使用path.GetFileName获取一个带路径的全文件名。
//通过HtmlEncode进行编码的结果必须在错误消息中返回。
var fileName = WebUtility.HtmlEncode(Path.GetFileName(formFile.FileName)); if (formFile.ContentType.ToLower() != "text/plain")
{
modelState.AddModelError(formFile.Name,
$"The {fieldDisplayName}file ({fileName}) must be a text file.");
} //校验文件长度,如果文件不包含内容,则不必读取文件长度。
//此校验不会检查仅具有BOM(字节顺序标记)作为内容的文件,
//因此在读取文件内容后再次检验文件内容长度,以校验仅包含BOM的文件。
if (formFile.Length == )
{
modelState.AddModelError(formFile.Name, $"The {fieldDisplayName}file ({fileName}) is empty.");
}
else if (formFile.Length > )
{
modelState.AddModelError(formFile.Name, $"The {fieldDisplayName}file ({fileName}) exceeds 1 MB.");
}
else
{
try
{
string fileContents; //使用StreamReader按UTF-8编码读取文件。
//如果上传文件是采用其他的编码。
//请使用32位编码,将UTF8Encoding改为UTF32Encoding
using (
var reader =
new StreamReader(
formFile.OpenReadStream(),
new UTF32Encoding(),
detectEncodingFromByteOrderMarks: true))
{
fileContents = await reader.ReadToEndAsync(); // 检查文件长度,如果文件的唯一内容是BOM,在删除BOM后内容实际上是空的。
if (fileContents.Length > )
{
return fileContents;
}
else
{
modelState.AddModelError(formFile.Name,
$"The {fieldDisplayName}file ({fileName}) is empty.");
}
}
}
catch (Exception ex)
{
modelState.AddModelError(formFile.Name,
$"The {fieldDisplayName}file ({fileName}) upload failed. " +
$"Please contact the Help Desk for support. Error: {ex.Message}");
// Log the exception
}
} return string.Empty;
}
}
}
三、添加Describe类
在Visual Studio 2017的解决方案资源管理器中,鼠标左键选中“Models”文件夹,右键单击,在弹出菜单中选择“添加—>类”(如下图)。 将类命名为“Describe”,并添加以下属性,代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks; namespace RazorMvcBooks.Models
{ public class Describe
{ public int ID { get; set; } [Display(Name = "文件名称")]
public string Name { get; set; }
[Display(Name = "公共描述")]
public string PublicDescribe { get; set; } [Display(Name = "公共描述大小(bytes)")]
[DisplayFormat(DataFormatString = "{0:N1}")]
public long PublicScheduleSize { get; set; } [Display(Name = "后台描述")]
public string PrivateDescribe { get; set; } [Display(Name = "后台描述大小 (bytes)")]
[DisplayFormat(DataFormatString = "{0:N1}")]
public long PrivateScheduleSize { get; set; } [Display(Name = "上传时间(UTC)")]
[DisplayFormat(DataFormatString = "{0:F}")]
public DateTime UploadDateTime { get; set; } } }
此类使用 Display 和 DisplayFormat 特性,有前端显示时,这些特性会生成友好的标题和格式。
四、修改BookContext
在Visual Studio 2017中打开BookContext (Models/BookContext.cs) 文件,并修改代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; namespace RazorMvcBooks.Models
{
public class BookContext:DbContext
{
public BookContext(DbContextOptions<BookContext> options)
: base(options)
{
} public DbSet<Book> Book { get; set; }
public DbSet<Describe> Describe { get; set; } }
}
五、将 “描述信息” 表添加到数据库
在Visual Studio 2017中打开包管理器控制台 (PMC):“工具” > “NuGet 包管理器” > “包管理器控制台”。

在 PMC 中执行以下命令。 这些命令将向数据库添加 Describe 表,执行结果发下图:
Add-Migration AddDescribeTable
Update-Database

在执行以上指令之后,会在数据库中添加Describe表,结果如下图。

学习ASP.NET Core Razor 编程系列十三——文件上传功能(一)的更多相关文章
- 学习ASP.NET Core Razor 编程系列十九——分页
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- 学习ASP.NET Core Razor 编程系列十八——并发解决方案
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- 学习ASP.NET Core Razor 编程系列十七——分组
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- 学习ASP.NET Core Razor 编程系列十六——排序
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- 学习ASP.NET Core Razor 编程系列十五——文件上传功能(三)
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- 学习ASP.NET Core Razor 编程系列十四——文件上传功能(二)
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- 学习ASP.NET Core Razor 编程系列二——添加一个实体
在Razor页面应用程序中添加一个实体 在本篇文章中,学习添加用于管理数据库中的书籍的实体类.通过实体框架(EF Core)使用这些类来处理数据库.EF Core是一个对象关系映射(ORM)框架,它简 ...
- 学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- 学习ASP.NET Core Razor 编程系列五——Asp.Net Core Razor新建模板页面
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
随机推荐
- BZOJ_4872_[Shoi2017]分手是祝愿_概率与期望
BZOJ_4872_[Shoi2017]分手是祝愿_概率与期望 Description Zeit und Raum trennen dich und mich. 时空将你我分开.B 君在玩一个游戏,这 ...
- Redis详解(一)------ redis的简介与安装
工作中一直在用 Redis,但是一直没有进行系统的总结,这个系列的博客将整体的介绍 Redis 的用法. 1.Redis 的简介 Redis:REmote DIctionary Server(远程字典 ...
- Applet web端对文件的读取方式
转载:http://www.exam8.com/computer/Java/zonghe/200708/659876.html ---- 我们知道,在Java Applet中出于安全性考虑,Apple ...
- ISCC2018(最新的考核解析)
最近一直在做这个 ISCC2018,感觉可能自己只是一个新手吧!但是我会继续努力的,希望我的解题思路能够给你们带来一定的想法,我也希望自己能够在安全方面遇到更多志同道合的人! 其它题目可以看这里 1 ...
- Java开源生鲜电商平台-通知模块设计与架构(源码可下载)
Java开源生鲜电商平台-通知模块设计与架构(源码可下载) 说明:对于一个生鲜的B2B平台而言,通知对于我们实际的运营而言来讲分为三种方式: 1. 消息推送:(采用极光推送) ...
- STM32学习笔记(二):GPIO口工作原理
STM32每个IO口具有7个寄存器来控制,每个IO口都可以自由进行编程控制,我们编程实际上控制的是通过控制那7个寄存器来控制我们的IO口,我们可以通过编程控制IO口,把IO口配置成如下八种模式: 1. ...
- HTML5网页录音和上传到服务器,支持PC、Android,支持IOS微信
准备做一个网页版聊天界面,表情啊.图片啊.上传文件啊都应该要有,视频就算了,语音还是要的. 本文记录的是在网页上用GitHub上的Recorder进行在线录音和上传到服务器,前几天升了一下级,以后有时 ...
- DBA_OBJECTS
类型:View Owner:SYS 内容:记录了数据库中所有的对象 字段: OWNER:对象的Owner OBJECT_NAME:对象名称 SUBOBJECT_NAME:对象的子对象名字,例如分区 O ...
- Python爬虫入门教程 62-100 30岁了,想找点文献提高自己,还被反爬了,Python搞起,反爬第2篇
学术搜索 学习理论的知识少不了去检索文献,好多文献为你的实操提供了合理的支撑,我所在的大学内网默认是有知网账户的,非常NICE 今天要完成的网站是 http://ac.scmor.com/ Googl ...
- String求求你别秀了
小鲁班今年计算机专业大四了,在学校可学了不少软件开发的东西,也自学了一些JAVA的后台框架,踌躇满志,一心想着找个好单位实习.当投递了无数份简历后,终于收到了一个公司发来的面试通知,小鲁班欣喜若狂. ...