导入导出实在多例子,很多成熟的组建都分装了导入和导出,这一节演示利用LinqToExcel组件对Excel的导入,这个是一个极其简单的例子。
我并不是说导入的简单。而是LinqToExcel让我们对Excel操作更加简单!
最后我们将利用ClosedXML输出Excel。这个比现流行NPOI与EPPlus更加优秀的组件,以Open XML SDK为基础,所以只支持xlsx,不支持xls格式(现阶段谁没有个office2007以上版本)
他导出的Excel根据官方描述,兼容性远超同行对手
如果你不是使用本架构只看2,3,4点,使用BLL层的代码,这同样适用你的MVC程序
知识点:
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)
因为我们读取Excel放在BLL层,所有在BLL层安装LinqToExcel包
(这一点简单带过,可以到网上下载上传代码植入到自己系统中)
或者下载第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="255" 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');
});
保证上传是成功的。
-------------------------------------------------------------------------------------------------------上面只是前期的准备工作--------------------------------------------------------------
在业务层添加以下代码
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; } } } }
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; } } }
代码包含两个方法
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(1, Resource.InsertSucceed)); } else { string ErrorCol = errors.Error; LogHandler.WriteServiceLog(GetUserId(), ErrorCol, "失败", "导入", "Spl_Person"); return Json(JsonHandler.CreateMessage(0, Resource.InsertFail + ErrorCol)); } }
最后前端还需要把路径给回来。
function ImportData() { $.post("@Url.Action("Import")?filePath=" + $("#txtExcelPath").val(), function (data) { if (data.type == 1) { $("#List").datagrid('load'); $('#uploadExcel').window('close'); } $.messageBox5s('@Resource.Tip', data.message); }, "json"); }
OK测试一下!建立一个新的excel格式
一般情况下我们是提供模版给用户下载供用户输入数据,来确保格式的正确性
虽然做好了导出功能,但是来不及发代码。只能到下次有时间再分析导出功能
本节知识点,全部聚集在CheckImportData方法上。
对应列头是模版xlsx的列头
1.如果模版需要是是中文的,如Name=名字,那么方法应该这么写
excelFile.AddMapping<Spl_PersonModel>(x => x.Name, "名字");
2.导入第几个sheet工作薄可以这么写
我这里写0是指第一个sheet工作薄。可以直接指定工作薄
var excelContent = excelFile.Worksheet<Spl_PersonModel>("Sheet1");
3.检查正确性可以确保数据的来源。可以给出用户正确的修改提示。