先上一张小弟的思路图:
(说明:没有安装做流程图的软件!凑合着看吧)
2 进入正题
首先跟着小弟先创建一个默认的MVC项目(相信大家都会创建,这里就不演示了)
第一步
创建一个实体类,这个类是要导入数据库的对象。为了简单起见 小弟创建了个学生对象(记录了他的名字、年龄、各科成绩分数)。
先声明这只是教学代码,完全是为了演示用的,你真正做产品的代码可没这么简单的定义实体。
public class Student
{
public Student()
{
Id = Guid.NewGuid();
}
//姓名
public string Name { get; set; }
//年龄
public int Age { get; set; }
//语文成绩
public int ChineseScore { get; set; }
//英文成绩
public int EnglishScore { get; set; }
//数学成绩
public int MathScore { get; set; }
24
}
第二步
实体创建完了,接下来我们创建一个Empty的控制器(名字取自己喜欢的),(这个也太简单了,我这里就不演示了)直接上结果:
public class UploadExcelController : Controller
{
// GET: /UploadExcel
public ActionResult Index()
{
return View();
}
}
第三步
控制器创建完了接下来做什么呢?估计你也猜到了,所有的不是从界面开始吗!用户访问/UploadExcel/Index 的时候总要出来个交互的东西吧。那就先创建一个交互的视图,光标移到return View();右键创建添加视图
创建视图有很多种方法,我这里只是选择了种简单的具体看个人习惯创建。
视图创建完毕后,我们先构造一个浏览文件的html标记
@{
ViewBag.Title = "View";
}
@*浏览本地文件按钮*@
浏览文件
小弟这里使用的是MVC自带的bootstrap框架,相信大家也都能看懂,如果看不懂那些标记的话!建议你先花1天时间入门bootstap框架。
在上传文件到服务器有个常用的html标记是 ,没错小弟也用的这个标记,只是它太丑了我把它给隐藏了!用我上面的html标记给替换了。接下来我们开始创建它。
@*浏览本地文件按钮*@
浏览文件
@using (Html.BeginForm("Browse", "UploadExcel", FormMethod.Post, new { enctype = "multipart/form-data", id = "form_Upload" }))
{
@Html.AntiForgeryToken() //防止跨站请求伪造(CSRF:Cross-site request forgery)攻击
//把fileUpload隐藏,原因它太难看了
}
红色的代码是新增的,Html.BeginForm的扩展方法如果大家不熟悉的话,你就把它看成form表单,因为它最后会生成form表单
哦!这个是基础知识,相信大家也都知道,(题外话:在能使用@HTML扩展方法的时候,我建议大家不要使用原生的HTML标记,为什么呢?因为如果你了解@HTML扩展方法的运行机制的话你就知道我的用意了, 就比如Html.BeginForm它是根据路由去生成的URL,智能的!安全的!如果自己写原生HTML标记的话,难免会产生不安全的URL!)
这个form表单就是我们把Excel文件上传到服务器用的,
看见form表单的 action="/UploadExcel/Browse",所以我们还要在UploadExcelController控制器里创建第二个名称叫Browse的Action操作
public class UploadExcelController : Controller
{
// GET: /UploadExcel
public ActionResult Index()
{
return View();
}
10
[HttpPost]
[ValidateAntiForgeryToken]
[HandleError(View = "~/Views/Shared/Error.cshtml")]
public ActionResult Browse(HttpPostedFileBase file)
{
}
}
红色部分是我新增加的Action操作,前面说了竟然创建的form表单是上传文件到服务器的,那么这个操作就是处理上传文件用的Action,因为它会改变服务器状态,所以我定义成Post方法。其余2个特性是配合@Html扩展方法用的,一个是防止跨站请求伪造(CSRF:Cross-site request forgery)攻击,一个是统一处理异常页。你也可以不加,跟我们今天的例子没关系。我们先不写这个Action操作的代码,让我们再次回到我们创建的视图页 。
竟然我们要使用 的功能,但是又嫌弃它太难看了!怎么办呢? So easy 只需把我们构造的html标记绑定它就行了。在视图页添加如下JS代码
@section scripts{
}
这段JS代码功能就是在选择文件后就把文件路径赋给我们自己构造的文本框里,然后就是提交表单,上传它的选择的文件到我们的服务器。
请注意灰色那段代码是如何用我们自己构建的按钮绑定到fileUpload的功能上
1 @*浏览本地文件按钮*@
2
3 浏览文件
4
为了浏览文件有响应的效果,我们在视图页加一小段CSS代码
@section scripts{
}
第五步
到了这里我们的文件已经可以上传到我们服务了,如果不信你在Browse操作打个断点,看看file参数是不是已经接受了文件,如果接受到了说明已经成功一半了!我们还是先不写Browse操作处理Excel文件的代码,焦点还是在视图页上,在本博客第一张效果图里,大家看到浏览文件下面有张table表格吗?小弟创建这个表格只是为了更好的交互效果,让使用的人更直观而已。而且也很简单!
接下来我们来构建它,在视图页新增table表格的代码
@model Student
@using School.Entity
@Html.DisplayNameFor(model => model.Name) | @Html.DisplayNameFor(model => model.Age) | @Html.DisplayNameFor(model => model.ChineseScore) | @Html.DisplayNameFor(model => model.EnglishScore) | @Html.DisplayNameFor(model => model.MathScore) |
---|---|---|---|---|
@Html.DisplayFor(model => item.Name) | @Html.DisplayFor(model => item.Age) | @Html.DisplayFor(model => item.ChineseScore) | @Html.DisplayFor(model => item.EnglishScore) | @Html.DisplayFor(model => item.MathScore) |
在这里我依然用的@HTML辅助方法,如果你还不会使用它,赶紧花一天学习它,入门非常简单!非常强大! 设想如果没有@Html扩展方法 小弟得写多少硬编码啊!
这里的表头是英文的,如果你想变成中文的话,可以在实体上加上数据注解特性(如下)
public class Student
{
[Display(Name="中文成绩")]
public int ChineseScore { get; set; }
}
对了还忘了一个东西,就是上传提交按钮,我们现在来构建它!在视图页form表单下面添加如下代码
@using (Html.BeginForm("Browse", "UploadExcel", FormMethod.Post, new { enctype = "multipart/form-data", id = "form_Upload" }))
{
@Html.AntiForgeryToken()
}
//红色部分是我构建的上传提交按钮
@Html.RouteLink扩展方法会根据我定义的路由生成一个锚标签,最后生成如下html标记
在这里我把它伪装成了一个button按钮
data-style="expand-right"这些属性是我用bootstrap加了个5毛钱的特效,你也可以不用管,也可以使用自己的特效。这个上传提交按钮的功能就是最后一个功能,把经过Browse操作转换成List
public class UploadExcelController : Controller
{
// GET: /UploadExcel
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
[HandleError(View = "~/Views/Shared/Error.cshtml")]
public ActionResult Browse(HttpPostedFileBase file)
{
return null;
}
//红色部分是我新增的Action操作,这个操作的作用是把Browse操作转换好的List
[HandleError(View = "~/Views/Shared/Error.cshtml")]
public ActionResult Upload()
{
return View("UploadSuccess"); //导入成功的页面 这个页面就留给大家自己设计吧
}
}
现在我们把重点放在Excel文件的逻辑处理上了,我们先从Browse操作下手,因为此操作负责把我们上传的Excel文件转换成List Entity对象,只要转换成这个集合对象你后面就可以想怎么插入就怎么插入了 !想插入MSSQL MYSQL 等不同数据都可以呵呵!因为我们用的ORM框架!
按照我上传的那个思维图,我想我先处理验证!先判断文件的格式是不是Excel的格式。(Excel的格式是根据版本来的 2007-2010 是xlsx,2003是xls)这里我只默认了2007-2010 。
在Browse操作添加如下代码
[HttpPost]
[ValidateAntiForgeryToken]
[HandleError(View = "~/Views/Shared/Error.cshtml")]
public ActionResult Browse(HttpPostedFileBase file)
{
if (string.Empty.Equals(file.FileName) || ".xlsx" != Path.GetExtension(file.FileName))
{
throw new ArgumentException("当前文件格式不正确,请确保正确的Excel文件格式!");
}
var severPath = this.Server.MapPath("/files/"); //获取当前虚拟文件路径
var savePath = Path.Combine(severPath, file.FileName); //拼接保存文件路径
try
{
file.SaveAs(savePath);
stus = ExcelHelper.ReadExcelToEntityList
ViewBag.Data = stus;
return View("Index");
}
finally
{
System.IO.File.Delete(savePath);//每次上传完毕删除文件
}
}
我在MVC项目的根目录创建了个files的文件夹,用来保存上传的Excel文件。然后读取文件转换成List Entity对象,然后把它传给我们创建的视图。这样就可以一选择Excel文件就把数据显示在页面上了,转换数据的核心是这句代码
stus = ExcelHelper.ReadExcelToEntityList
ExcelHelper是我自己封装的一个工具库,我现在来创建它。在根目录添加一个文件夹,然后添加一个类
ppublic class ExcelHelper
{
//Excel数据转List
public static IList
{
DataTable tbl = ReadExcelToDataTable(filePath);//读取Excel数据到DataTable
IList
return list;
}
//Excel数据转DataTable 使用的oledb读取方式
public static DataTable ReadExcelToDataTable(string filePath)
{
if (filePath == string.Empty) throw new ArgumentNullException("路径参数不能为空");
string ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0; Persist Security Info=False;Data Source=" + filePath + "; Extended Properties='Excel 8.0;HDR=Yes;IMEX=1'";
OleDbDataAdapter adapter = new OleDbDataAdapter("select * From[Sheet1$]", ConnectionString); //默认读取的Sheet1,你也可以把它封装变量,动态读取你的Sheet工作表
DataTable table = new DataTable("TempTable");
adapter.Fill(table);
return table;
}
//DataTable转List
public static List
{
if (dt == null) return null;
List
//遍历DataTable中所有的数据行
foreach (DataRow dr in dt.Rows)
{
T t = new T();
PropertyInfo[] propertys = t.GetType().GetProperties();
foreach (PropertyInfo pro in propertys)
{
//检查DataTable是否包含此列(列名==对象的属性名)
if (dt.Columns.Contains(pro.Name))
{
object value = dr[pro.Name];
value = Convert.ChangeType(value, pro.PropertyType);//强制转换类型
//如果非空,则赋给对象的属性 PropertyInfo
if (value != DBNull.Value)
{
pro.SetValue(t, value, null);
}
}
}
//对象添加到泛型集合中
list.Add(t);
}
return list;
}
}
代码很简单我就不翻译了,就是读取Excel数据转各种C#对象,但是这是教学代码不是产品代码,我很粗暴的封装了。你如果要用到生成环境,得还要加上各种逻辑验证和测试!
写到这里,感觉最后一个功能把List
我贴最后导入数据库的代码就是Upload操作,我在DAL层是使用了事物处理的,支持回滚!
[HandleError(View = "~/Views/Shared/Error.cshtml")]
public ActionResult Upload()
{
var result= Ioc.Service.IocBll
if(string.Empty!=result.Success)
ViewBag.Info = result.Info;
return View("UploadSuccess");
}