ASP.NET MVC5+EF6+EasyUI 后台管理系统(63)-Excel导入和导出
系列目录
昨天文章太过仓促没有补充导出的示例源码,在者当时弄到到很晚没时间做出导出功能,对阅读理解造成影响,现补充一份示例源码,顺便补充导出的功能说明,望理解
示例代码下载 https://yunpan.cn/cRTHt5MuKavwH 访问密码 0a47
ps:Vs数据库脚本在解压目录下,修改web.config数据库链接,示例代码包含:导入,导出,上传
前言:
导入导出实在多例子,很多成熟的组建都分装了导入和导出,这一节演示利用LinqToExcel组件对Excel的导入,这个是一个极其简单的例子。
我并不是说导入的简单。而是LinqToExcel让我们对Excel操作更加简单!
最后我们将利用ClosedXML输出Excel。这个比现流行NPOI与EPPlus更加优秀的组件,以Open XML SDK为基础,所以只支持xlsx,不支持xls格式(现阶段谁没有个office2007以上版本)
他导出的Excel根据官方描述,兼容性远超同行对手
如果你不是使用本架构只看2,3,4点,使用BLL层的代码,这同样适用你的MVC程序
知识点:
- LinqToExcel组件读取Excel文件
- ClosedXML组件输出Excel
准备:
- 一张演示的数据库表
- 安装LinqToExcel NuGet包
- 文件上传样例
- CloseXML导出Excel
开始:
1.数据表
CREATE TABLE [dbo].[Spl_Person](
[Id] [nvarchar](50) NOT NULL, --ID
[Name] [nvarchar](50) NULL, --姓名
[Sex] [nchar](10) NULL, --性别
[Age] [int] NULL, --年龄
[IDCard] [nvarchar](50) NULL, --IDCard
[Phone] [nvarchar](50) NULL, --电话
[Email] [nvarchar](200) NULL, --邮件
[Address] [nvarchar](300) NULL, --地址
[CreateTime] [datetime] NOT NULL, --创建时间
[Region] [nvarchar](50) NULL, --区域
[Category] [nvarchar](50) NULL, --类别
CONSTRAINT [PK_Spl_Person] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] GO
按照之前的做法,更新到EF。并利用T4生成DAL,BLL,MODEL。再用代码生成器生成界面复制进解决方案,一步到位
配置好访问地址和权限,直接运行

再手动在工具栏添加导入和导出的按钮(别忘记添加权限)

@Html.ToolButton("btnImport", "fa fa-level-down", Resource.Import, perm, "Import", true)
@Html.ToolButton("btnExport", "fa fa-level-up", Resource.Export, perm, "Export", true)
2.安装LinqToExcel包
因为我们读取Excel放在BLL层,所有在BLL层安装LinqToExcel包

3.文件上传
(这一点简单带过,可以到网上下载上传代码植入到自己系统中)
或者下载第32节的源码 或者下载本节的示例代码都可以
我这里使用普通的form上传功能
添加导入前端代码
<div id="uploadExcel" class="easyui-window" data-options="modal:true,closed:true,minimizable:false,shadow:false">
<form name="form1" method="post" id="form1">
<table>
<tr>
<th style=" padding:20px;">Excel:</th>
<td style=" padding:20px;">
<input name="ExcelPath" type="text" maxlength="" id="txtExcelPath" readonly="readonly" style="width:200px" class="txtInput normal left">
<a href="javascript:$('#FileUpload').trigger('click').void(0);;" class="files">@Resource.Browse</a>
<input class="displaynone" type="file" id="FileUpload" name="FileUpload" onchange="Upload('ExcelFile', 'txtExcelPath', 'FileUpload');">
<span class="uploading">@Resource.Uploading</span>
</td>
</tr>
</table>
<div class="endbtndiv">
<a id="btnSave" href="javascript:ImportData()" class="easyui-linkbutton btns">Save</a>
<a id="btnReturn" href="javascript:$('#uploadExcel').window('close')" class="easyui-linkbutton btnc">Close</a>
</div>
</form>
</div>
导入按钮事件只要弹出上传框就好
$("#btnImport").click(function () {
$("#uploadExcel").window({ title: '@Resource.Import', width: 450, height: 160, iconCls: 'icon-details' }).window('open');
});
保证上传是成功的。

直接查看源码的C#上传代码
-------------------------------------------------------------------------------------------------------上面只是前期的准备工作--------------------------------------------------------------
在业务层添加以下代码
using Apps.Common;
using Apps.Models;
using Apps.Models.Spl;
using LinqToExcel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Apps.Spl.BLL
{
public partial class Spl_ProductBLL
{
/// <summary>
/// 校验Excel数据
/// </summary>
public bool CheckImportData( string fileName, List<Spl_PersonModel> personList,ref ValidationErrors errors )
{ var targetFile = new FileInfo(fileName); if (!targetFile.Exists)
{ errors.Add("导入的数据文件不存在");
return false;
} var excelFile = new ExcelQueryFactory(fileName); //对应列头
excelFile.AddMapping<Spl_PersonModel>(x => x.Name, "Name");
excelFile.AddMapping<Spl_PersonModel>(x => x.Sex, "Sex");
excelFile.AddMapping<Spl_PersonModel>(x => x.Age, "Age");
excelFile.AddMapping<Spl_PersonModel>(x => x.IDCard, "IDCard");
excelFile.AddMapping<Spl_PersonModel>(x => x.Phone, "Phone");
excelFile.AddMapping<Spl_PersonModel>(x => x.Email, "Email");
excelFile.AddMapping<Spl_PersonModel>(x => x.Address, "Address");
excelFile.AddMapping<Spl_PersonModel>(x => x.Region, "Region");
excelFile.AddMapping<Spl_PersonModel>(x => x.Category, "Category");
//SheetName
var excelContent = excelFile.Worksheet<Spl_PersonModel>(0); int rowIndex = 1; //检查数据正确性
foreach (var row in excelContent)
{
var errorMessage = new StringBuilder();
var person = new Spl_PersonModel(); person.Id =
person.Name = row.Name;
person.Sex = row.Sex;
person.Age = row.Age;
person.IDCard = row.IDCard;
person.Phone = row.Phone;
person.Email = row.Email;
person.Address = row.Address;
person.Region = row.Region;
person.Category = row.Category; if (string.IsNullOrWhiteSpace(row.Name))
{
errorMessage.Append("Name - 不能为空. ");
} if (string.IsNullOrWhiteSpace(row.IDCard))
{
errorMessage.Append("IDCard - 不能为空. ");
} //=============================================================================
if (errorMessage.Length > 0)
{
errors.Add(string.Format(
"第 {0} 列发现错误:{1}{2}",
rowIndex,
errorMessage,
"<br/>"));
}
personList.Add(person);
rowIndex += 1;
}
if (errors.Count > 0)
{
return false;
}
return true;
} /// <summary>
/// 保存数据
/// </summary>
public void SaveImportData(IEnumerable<Spl_PersonModel> personList)
{
try
{
DBContainer db = new DBContainer();
foreach (var model in personList)
{
Spl_Person entity = new Spl_Person();
entity.Id = ResultHelper.NewId;
entity.Name = model.Name;
entity.Sex = model.Sex;
entity.Age = model.Age;
entity.IDCard = model.IDCard;
entity.Phone = model.Phone;
entity.Email = model.Email;
entity.Address = model.Address;
entity.CreateTime = ResultHelper.NowTime;
entity.Region = model.Region;
entity.Category = model.Category;
db.Spl_Person.Add(entity);
}
db.SaveChanges();
}
catch (Exception ex)
{
throw;
}
}
}
}
BLL
public class ValidationErrors : List<ValidationError>
{
/// <summary>
/// 添加错误
/// </summary>
/// <param name="errorMessage">信息描述</param>
public void Add(string errorMessage)
{
base.Add(new ValidationError { ErrorMessage = errorMessage });
}
/// <summary>
/// 获取错误集合
/// </summary>
public string Error
{
get {
string error = "";
this.All(a => {
error += a.ErrorMessage;
return true;
});
return error;
}
}
}
ValidationError
代码包含两个方法
public bool CheckImportData( string fileName, List<Spl_PersonModel> personList,ValidationErrors errors )
fileName为我们上传的文件。
personList为承接数据List
ValidationErrors 错误集合
public void SaveImportData(IEnumerable<Spl_PersonModel> personList)
保存数据
别忘记添加接口
public partial interface ISpl_PersonBLL
{
bool CheckImportData(string fileName, List<Spl_PersonModel> personList, ref ValidationErrors errors);
void SaveImportData(IEnumerable<Spl_PersonModel> personList);
}
简单明白,直接看代码,不再解析。OK这样控制器就可以直接调用了
public ActionResult Import(string filePath)
{
var personList = new List<Spl_PersonModel>();
//校验数据is
bool checkResult = m_BLL.CheckImportData(filePath, personList, ref errors);
//校验通过直接保存
if (checkResult)
{
m_BLL.SaveImportData(personList);
LogHandler.WriteServiceLog(GetUserId(),"导入成功", "成功", "导入", "Spl_Person");
return Json(JsonHandler.CreateMessage(, Resource.InsertSucceed));
}
else
{
string ErrorCol = errors.Error;
LogHandler.WriteServiceLog(GetUserId(), ErrorCol, "失败", "导入", "Spl_Person");
return Json(JsonHandler.CreateMessage(, Resource.InsertFail + ErrorCol));
} }
最后前端还需要把路径给回来。
function ImportData()
{
$.post("@Url.Action("Import")?filePath=" + $("#txtExcelPath").val(), function (data) {
if (data.type == ) {
$("#List").datagrid('load');
$('#uploadExcel').window('close');
}
$.messageBox5s('@Resource.Tip', data.message); }, "json");
}
OK测试一下!建立一个新的excel格式

一般情况下我们是提供模版给用户下载供用户输入数据,来确保格式的正确性

--------------------------------------------------------------------------------------导出功能------------------------------------------------------------------------------
4.安装ClosedXML NuGet包

在控制器添加以下代码:
public ActionResult Export()
{
var exportSpource = this.GetExportData();
var dt = JsonConvert.DeserializeObject<DataTable>(exportSpource.ToString()); var exportFileName = string.Concat(
"Person",
DateTime.Now.ToString("yyyyMMddHHmmss"),
".xlsx"); return new ExportExcelResult
{
SheetName = "人员列表",
FileName = exportFileName,
ExportData = dt
};
} private JArray GetExportData()
{
List<Spl_PersonModel> list = m_BLL.GetList(ref setNoPagerAscById, "");
JArray jObjects = new JArray(); foreach (var item in list)
{
var jo = new JObject();
jo.Add("Id", item.Id);
jo.Add("Name", item.Name);
jo.Add("Sex", item.Sex);
jo.Add("Age", item.Age);
jo.Add("IDCard", item.IDCard);
jo.Add("Phone", item.Phone);
jo.Add("Email", item.Email);
jo.Add("Address", item.Address);
jo.Add("CreateTime", item.CreateTime);
jo.Add("Region", item.Region);
jo.Add("Category", item.Category);
jObjects.Add(jo);
}
return jObjects;
}
注意:ExportExcelResult
此类是使用ClosedXML.Excel,已经封装好了。大家直接拿来用就可以。把关注点都放在业务中
using ClosedXML.Excel;
using System;
using System.Data;
using System.IO;
using System.Text;
using System.Web;
using System.Web.Mvc; namespace Apps.Web.Core
{
public class ExportExcelResult : ActionResult
{
public string SheetName { get; set; }
public string FileName { get; set; }
public DataTable ExportData { get; set; } public ExportExcelResult()
{ } public override void ExecuteResult(ControllerContext context)
{
if (ExportData == null)
{
throw new InvalidDataException("ExportData");
}
if (string.IsNullOrWhiteSpace(this.SheetName))
{
this.SheetName = "Sheet1";
}
if (string.IsNullOrWhiteSpace(this.FileName))
{
this.FileName = string.Concat(
"ExportData_",
DateTime.Now.ToString("yyyyMMddHHmmss"),
".xlsx");
} this.ExportExcelEventHandler(context);
} /// <summary>
/// Exports the excel event handler.
/// </summary>
/// <param name="context">The context.</param>
private void ExportExcelEventHandler(ControllerContext context)
{
try
{
var workbook = new XLWorkbook(); if (this.ExportData != null)
{
context.HttpContext.Response.Clear(); // 编码
context.HttpContext.Response.ContentEncoding = Encoding.UTF8; // 设置网页ContentType
context.HttpContext.Response.ContentType =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; // 导出名字
var browser = context.HttpContext.Request.Browser.Browser;
var exportFileName = browser.Equals("Firefox", StringComparison.OrdinalIgnoreCase)
? this.FileName
: HttpUtility.UrlEncode(this.FileName, Encoding.UTF8); context.HttpContext.Response.AddHeader(
"Content-Disposition",
string.Format("attachment;filename={0}", exportFileName)); // Add all DataTables in the DataSet as a worksheets
workbook.Worksheets.Add(this.ExportData, this.SheetName); using (var memoryStream = new MemoryStream())
{
workbook.SaveAs(memoryStream);
memoryStream.WriteTo(context.HttpContext.Response.OutputStream);
memoryStream.Close();
}
}
workbook.Dispose();
}
catch (Exception ex)
{
throw;
}
}
}
}
总结:
本节知识点,全部聚集在CheckImportData方法上。
对应列头是模版xlsx的列头

1.如果模版需要是是中文的,如Name=名字,那么方法应该这么写
excelFile.AddMapping<Spl_PersonModel>(x => x.Name, "名字");
2.导入第几个sheet工作薄可以这么写
我这里写0是指第一个sheet工作薄。可以直接指定工作薄
var excelContent = excelFile.Worksheet<Spl_PersonModel>("Sheet1");
3.检查正确性可以确保数据的来源。可以给出用户正确的修改提示。
4.借助ClosedXML,导出实际只需要几行代码。哈哈..这是如此的简单。
return new ExportExcelResult
{
SheetName = "人员列表",
FileName = exportFileName,
ExportData = dt
};

2017-03-08 新增功能:导入前编辑,在导入之前会弹出编辑行的页面,让用户编辑数据或新增数据后决定是否提交

谢谢大家
ASP.NET MVC5+EF6+EasyUI 后台管理系统(63)-Excel导入和导出的更多相关文章
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(63)-Excel导入和导出-自定义表模导入
系列目录 前言 上一节使用了LinqToExcel和CloseXML对Excel表进行导入和导出的简单操作,大家可以跳转到上一节查看: ASP.NET MVC5+EF6+EasyUI 后台管理系统(6 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-前言与目录(持续更新中...)
开发工具:VS2015(2012以上)+SQL2008R2以上数据库 您可以有偿获取一份最新源码联系QQ:729994997 价格 666RMB 升级后界面效果如下: 任务调度系统界面 http: ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-前言与目录(转)
开发工具:VS2015(2012以上)+SQL2008R2以上数据库 您可以有偿获取一份最新源码联系QQ:729994997 价格 666RMB 升级后界面效果如下: 日程管理 http://ww ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统-WebApi的用法与调试
1:ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-WebApi与Unity注入 使用Unity是为了使用我们后台的BLL和DAL层 2:ASP.NET MVC5+EF6+Easy ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(51)-系统升级
系统很久没有更新内容了,期待已久的更新在今天发布了,最近花了2个月的时间每天一点点,从原有系统 MVC4+EF5+UNITY2.X+Quartz 2.0+easyui 1.3.4无缝接入 MVC5+E ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(58)-DAL层重构
系列目录 前言:这是对本文系统一次重要的革新,很久就想要重构数据访问层了,数据访问层重复代码太多.主要集中增删该查每个模块都有,所以本次是为封装相同接口方法 如果你想了解怎么重构普通的接口DAL层请查 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(34)-文章发布系统①-简要分析
系列目录 最新比较闲,为了学习下Android的开发构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(1)-前言与,虽然有点没有目的的学习,但还是了解了Andro ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(54)-工作流设计-所有流程监控
系列目录 先补充一个平面化登陆页面代码,自己更换喜欢的颜色背景 @using Apps.Common; @{ Layout = null; } <!DOCTYPE html> <ht ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(56)-插件---单文件上传与easyui使用fancybox
系列目录 https://yunpan.cn/cZVeSJ33XSHKZ 访问密码 0fc2 今天整合lightbox插件Fancybox1.3.4,发现1.3.4版本太老了.而目前easyui 1 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(38)-Easyui-accordion+tree漂亮的菜单导航
系列目录 本节主要知识点是easyui 的手风琴加树结构做菜单导航 有园友抱怨原来菜单非常难看,但是基于原有树形无限级别的设计,没有办法只能已树形展示 先来看原来的效果 改变后的效果,当然我已经做好了 ...
随机推荐
- 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
- ASP.NET路由模型解析
大家好,我又来吹牛逼了 ~-_-~ 转载请注明出处:来自吹牛逼之<ASP.NET路由模型解析> 背景:很多人知道Asp.Net中路由怎么用的,却不知道路由模型内部的运行原理,今天我就给大家 ...
- Asp.net Boilerplate源码中NotNullAttribute的用处
看Asp.net Boilerplate 1.1.3.0源码时发现有一个NotNullAttribute的定义和27处的引用,就是不知道它的作用,当然顾名思义是可以的,就是不知道它是怎么判断的,在哪里 ...
- 【组织级项目管理】P2 MSP P3O
组织级项目管理--有你,有我,有大家 在过去的2年,无论对于企业来讲,还是对于我们个人都有很多大脑的冲击,有几个词大家应该特别耳熟能详:转型,变革,敏捷,互联网+,组织的项目化管理等.就是这些让我们的 ...
- Hyper-V 激活Windows系统重启后黑屏的解决方法 + 激活方法
异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 服务器相关的知识点:http://www.cnblogs.com/dunitia ...
- 谱聚类(spectral clustering)原理总结
谱聚类(spectral clustering)是广泛使用的聚类算法,比起传统的K-Means算法,谱聚类对数据分布的适应性更强,聚类效果也很优秀,同时聚类的计算量也小很多,更加难能可贵的是实现起来也 ...
- H5坦克大战之【玩家控制坦克移动2】
周一没有看圣诞大战,这几天比较忙也没有看赛后的报道,今天就先不扯NBA,随便扯扯自己.昨天在电脑里找东西的时候翻到以前兼职健身教练时的照片,思绪一下子回到学生时代,脑子久久换不过来.现在深深觉得健身和 ...
- IIC驱动移植在linux3.14.78上的实现和在linux2.6.29上实现对比(deep dive)
首先说明下为什么写这篇文章,网上有许多博客也是介绍I2C驱动在linux上移植的实现,但是笔者认为他们相当一部分没有分清所写的驱动时的驱动模型,是基于device tree, 还是基于传统的Platf ...
- C++随笔:.NET CoreCLR之corleCLR核心探索之coreconsole(1)
一看这个标题,是不去取名有点绕呢?或者是,还有些问题?报告LZ...你的标题取得有问题,是个病句!↖(^ω^)↗!!!先不要急,其实我今天带给大家的就是CoreCLR中的coreclr.其中它是在名字 ...
- PHP之Memcache缓存详解
Mem:memory缩写(内存):内存缓存 1. 断电或者重启服务器内存数据即消失,即临时数据: Memcache默认端口:11211 存入方式:key=>>value ...