结合bootstrap fileinput插件和Bootstrap-table表格插件,实现文件上传、预览、提交的导入Excel数据操作流程
1、bootstrap-fileinpu的简单介绍
在前面的随笔,我介绍了Bootstrap-table表格插件的具体项目应用过程,本篇随笔介绍另外一个Bootstrap FieInput插件的使用,整合两者可以实现我们常规的Web数据导入操作,导入数据操作过程包括有上传文件,预览数据,选择并提交记录等一系列操作。
关于这个插件,我在早期随笔《
Bootstrap文件上传插件File Input的使用
》也做了一次介绍,这是一个增强的 HTML5 文件输入控件,是一个 Bootstrap 3.x 的扩展,实现文件上传预览,多文件上传等功能。
bootstrap-fileinput源码:
https://github.com/kartik-v/bootstrap-fileinput
bootstrap-fileinput在线API:
http://plugins.krajee.com/file-input
bootstrap-fileinput Demo展示:
http://plugins.krajee.com/file-basic-usage-demo
这个插件主要是介绍如何处理图片上传的处理操作,原先我的Excel导入操作使用的是Uploadify插件,可以参考我随笔《
附件上传组件uploadify的使用
》,不过这个需要Flash控件支持,在某些浏览器(如Chrome)就比较麻烦了,因此决定使用一种较为通用的上传插件,这次首先对基于Bootstrap前端架构的框架系统进行升级,替代原来的Uploadify插件,这样页面上传功能,在各个浏览器就可以无差异的实现了。
一般情况下,我们需要引入下面两个文件,插件才能正常使用:
bootstrap-fileinput/css/fileinput.min.css
bootstrap-fileinput/js/fileinput.min.js
在File input 插件使用的时候,如果是基于Asp.NET MVC的,那么我们可以使用BundleConfig.cs进行添加对应的引用,加入到Bundles集合引用即可。
//添加对bootstrap-fileinput控件的支持 css_metronic.Include("~/Content/MyPlugins/bootstrap-fileinput/css/fileinput.min.css");
js_metronic.Include("~/Content/MyPlugins/bootstrap-fileinput/js/fileinput.min.js");
js_metronic.Include("~/Content/MyPlugins/bootstrap-fileinput/js/locales/zh.js");
在页面中,我们使用以下HTML代码实现界面展示,主要的bootstrap fileinput插件声明,主要是基础的界面代码
<inputid="excelFile"type="file">
Excel导入的的界面展示如下所示。
选择指定文件后,我们可以看到Excel的文件列表,如下界面所示。
上传文件后,数据直接展示在弹出层的列表里面,这里直接使用了 Bootstrap-table表格插件进行展示。
这样我们就可以把Excel的记录展示出来,实现了预览的功能,勾选必要的记录,然后保存即可提交到服务器进行保存,实现了Excel数据的真正导入数据库处理。
2、Excel导出操作详细介绍
我们在实际导入Excel的界面中,HTML代码如下所示。
<!--导入数据操作层--> <divid="import"class="modal fade bs-modal-lg"tabindex="-1"role="dialog"aria-labelledby="myModalLabel"aria-hidden="true"> <divclass="modal-dialog modal-lg"> <divclass="modal-content"> <divclass="modal-header bg-primary"> <buttontype="button"class="close"data-dismiss="modal"aria-hidden="true"></button> <h4class="modal-title">文件导入</h4> </div> <divclass="modal-body"> <divstyle="text-align:right;padding:5px"> <ahref="~/Content/Template/TestUser-模板.xls"onclick="javascript:Preview();"> <imgalt="测试用户信息-模板"src="~/Content/images/ico_excel.png" /> <spanstyle="font-size:larger;font-weight:200;color:red">TestUser-模板.xls</span> </a> </div> <hr/> <formid="ffImport"method="post"> <divtitle="Excel导入操作"style="padding: 5px"> <inputtype="hidden"id="AttachGUID"name="AttachGUID" /> <inputid="excelFile"type="file"> </div> </form> <!--数据显示表格--> <tableid="gridImport"class="table table-striped table-bordered table-hover"cellpadding="0"cellspacing="0"border="0"> </table> </div> <divclass="modal-footer"> <buttontype="button"class="btn btn-default"data-dismiss="modal">关闭</button> <buttontype="button"class="btn btn-primary"onclick="SaveImport()">保存</button> </div> </div> </div> </div>
对于bootstrap fileinput的各种属性,我们这里使用JS进行初始化,这样方便统一管理和修改。
//初始化Excel导入的文件 functionInitExcelFile() {//记录GUID $("#AttachGUID").val(newGuid());
$("#excelFile").fileinput({
uploadUrl:"/FileUpload/Upload",//上传的地址 uploadAsync: true, //异步上传 language: "zh", //设置语言 showCaption: true, //是否显示标题 showUpload: true, //是否显示上传按钮 showRemove: true, //是否显示移除按钮 showPreview : true, //是否显示预览按钮 browseClass: "btn btn-primary", //按钮样式 dropZoneEnabled: false, //是否显示拖拽区域 allowedFileExtensions: ["xls", "xlsx"], //接收的文件后缀 maxFileCount: 1, //最大上传文件数限制 previewFileIcon: '<i class="glyphicon glyphicon-file"></i>',
allowedPreviewTypes:null,
previewFileIconSettings: {'docx': '<i class="glyphicon glyphicon-file"></i>','xlsx': '<i class="glyphicon glyphicon-file"></i>','pptx': '<i class="glyphicon glyphicon-file"></i>','jpg': '<i class="glyphicon glyphicon-picture"></i>','pdf': '<i class="glyphicon glyphicon-file"></i>','zip': '<i class="glyphicon glyphicon-file"></i>',
},
uploadExtraData: {//上传的时候,增加的附加参数 folder: '数据导入文件', guid: $("#AttachGUID").val()
}
})//文件上传完成后的事件 .on('fileuploaded', function(event, data, previewId, index) {var form = data.form, files = data.files, extra =data.extra,
response= data.response, reader =data.reader;var res = data.response; //返回结果 if(res.Success) {
showTips('上传成功');var guid = $("#AttachGUID").val();//提示用户Excel格式是否正常,如果正常加载数据 $.ajax({
url:'/TestUser/CheckExcelColumns?guid=' +guid,
type:'get',
dataType:'json',
success:function(data) {if(data.Success) {
InitImport(guid);//重新刷新表格数据 showToast("文件已上传,数据加载完毕!");//重新刷新GUID,以及清空文件,方便下一次处理 RefreshExcel();
}else{
showToast("上传的Excel文件检查不通过。请根据页面右上角的Excel模板格式进行数据录入。", "error");
}
}
});
}else{
showTips('上传失败');
}
});
}
上面的逻辑具体就是,设置上传文件的后台页面为:/FileUpload/Upload,以及各种插件的配置参数,uploadExtraData里面设置的是提交的附加参数,也就是后台控制器接收的参数,其中
.on('fileuploaded', function (event, data, previewId, index) {
的函数处理文件上传后的处理函数,如果上传文件返回的结果是成功的,那么我们再次调用ajax来检查这个Excel的字段是否符合要求,如下地址:
url: '/TestUser/CheckExcelColumns?guid=' + guid,
如果这个检查的后台返回成功的记录,那么再次需要把Excel记录提取出来预览,并清空bootstrap fileinput文件上传插件,方便下次上传文件。如下代码所示。
if(data.Success) {
InitImport(guid);//重新刷新表格数据 showToast("文件已上传,数据加载完毕!");//重新刷新GUID,以及清空文件,方便下一次处理 RefreshExcel();
}else{
showToast("上传的Excel文件检查不通过。请根据页面右上角的Excel模板格式进行数据录入。", "error");
}
其中RefreshExcel就是重新更新上传的附加参数值,方便下次上传,否则附加参数的值一直不变化,就会导致我们设置的GUID没有变化而出现问题。
//重新更新GUID的值,并清空文件 functionRefreshExcel() {
$("#AttachGUID").val(newGuid());
$('#excelFile').fileinput('clear');//清空所有文件 //附加参数初始化后一直不会变化,如果需要发生变化,则需要使用refresh进行更新 $('#excelFile').fileinput('refresh', {
uploadExtraData: { folder:'数据导入文件', guid: $("#AttachGUID").val() },
});
}
而其中InitImport就是获取预览数据并展示在Bootstrap-table表格插件上的,关于这个插件的详细使用,可以回顾下随笔《
基于Metronic的Bootstrap开发框架经验总结(16)-- 使用插件bootstrap-table实现表格记录的查询、分页、排序等处理
》进行了解即可。
//根据条件查询并绑定结果 var$import;functionInitImport(guid) {var url = "/TestUser/GetExcelData?guid=" +guid;
$import= $('#gridImport').bootstrapTable({
url: url,//请求后台的URL(*) method: 'GET', //请求方式(*) striped: true, //是否显示行间隔色 cache: false, //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*) pagination: false, //是否显示分页(*) sidePagination: "server", //分页方式:client客户端分页,server服务端分页(*) pageNumber: 1, //初始化加载第一页,默认第一页,并记录 pageSize: 100, //每页的记录行数(*) pageList: [10, 25, 50, 100], //可供选择的每页的行数(*) search: false, //是否显示表格搜索 strictSearch: true,
showColumns:true, //是否显示所有的列(选择显示的列) showRefresh: true, //是否显示刷新按钮 minimumCountColumns: 2, //最少允许的列数 clickToSelect: true, //是否启用点击选中行 uniqueId: "ID", //每一行的唯一标识,一般为主键列 queryParams: function(params) { },
columns: [{
checkbox:true,
visible:true //是否显示复选框 }, {
field:'Name',
title:'姓名'}, {
field:'Mobile',
title:'手机'}, {
field:'Email',
title:'邮箱',
formatter: emailFormatter
}, {
field:'Homepage',
title:'主页',
formatter: linkFormatter
}, {
field:'Hobby',
title:'兴趣爱好'}, {
field:'Gender',
title:'性别',
formatter: sexFormatter
}, {
field:'Age',
title:'年龄'}, {
field:'BirthDate',
title:'出生日期',
formatter: dateFormatter
}, {
field:'Height',
title:'身高'}, {
field:'Note',
title:'备注'}],
onLoadSuccess:function() {
},
onLoadError:function() {
showTips("数据加载失败!");
},
});
}
最后就是确认提交后,会通过JS提交数据到后台进行处理,如下代码所示。
//保存导入的数据 function SaveImport() {var list = [];//构造集合对象 var rows = $import.bootstrapTable('getSelections');for (var i = 0; i < rows.length; i++) {
list.push({'Name': rows[i].Name, 'Mobile': rows[i].Mobile, 'Email': rows[i].Email, 'Homepage': rows[i].Homepage,'Hobby': rows[i].Hobby, 'Gender': rows[i].Gender, 'Age': rows[i].Age, 'BirthDate': rows[i].BirthDate,'Height': rows[i].Height, 'Note': rows[i].Note
});
}if (list.length == 0) {
showToast("请选择一条记录", "warning");return;
}var postData = { 'list': list };//可以增加其他参数,如{ 'list': list, 'Rucanghao': $("#Rucanghao").val() }; postData =JSON.stringify(postData);
$.ajax({
url:'/TestUser/SaveExcelData',
type:'post',
dataType:'json',
contentType:'application/json;charset=utf-8',
traditional:true,
success: function (data) {if(data.Success) {//保存成功 1.关闭弹出层,2.清空记录显示 3.刷新主列表 showToast("保存成功");
$("#import").modal("hide");
$(bodyTag).html("");
Refresh();
}else{
showToast("保存失败:" + data.ErrorMessage, "error");
}
},
data: postData
});
}
3、后台控制器代码分析
这里我们的JS代码里面,涉及了几个MVC后台的方法处理:Upload、CheckExcelColumns、GetExcelData、SaveExcelData。这里分别进行介绍。
文件上传的后台控制器方法如下所示。
/// <summary> ///上传附件到服务器上/// </summary> /// <param name="fileData">附件信息</param> /// <param name="guid">附件组GUID</param> /// <param name="folder">指定的上传目录</param> /// <returns></returns> [AcceptVerbs(HttpVerbs.Post)]public ActionResult Upload(string guid, stringfolder)
{
CommonResult result= newCommonResult();
HttpFileCollectionBase files=HttpContext.Request.Files;if (files != null)
{foreach (string key infiles.Keys)
{try{#region MyRegionHttpPostedFileBase fileData=files[key];if (fileData != null)
{
HttpContext.Request.ContentEncoding= Encoding.GetEncoding("UTF-8");
HttpContext.Response.ContentEncoding= Encoding.GetEncoding("UTF-8");
HttpContext.Response.Charset= "UTF-8";//文件上传后的保存路径 string filePath = Server.MapPath("~/UploadFiles/");
DirectoryUtil.AssertDirExist(filePath);string fileName = Path.GetFileName(fileData.FileName); //原始文件名称 string fileExtension = Path.GetExtension(fileName); //文件扩展名//string saveName = Guid.NewGuid().ToString() + fileExtension;//保存文件名称 FileUploadInfo info= newFileUploadInfo();
info.FileData=ReadFileBytes(fileData);if (info.FileData != null)
{
info.FileSize=info.FileData.Length;
}
info.Category=folder;
info.FileName=fileName;
info.FileExtend=fileExtension;
info.AttachmentGUID=guid;
info.AddTime=DateTime.Now;
info.Editor= CurrentUser.Name;//登录人 result= BLLFactory<FileUpload>.Instance.Upload(info);if (!result.Success)
{
LogTextHelper.Error("上传文件失败:" +result.ErrorMessage);
}
}#endregion}catch(Exception ex)
{
result.ErrorMessage=ex.Message;
LogTextHelper.Error(ex);
}
}
}else{
result.ErrorMessage= "fileData对象为空";
}returnToJsonContent(result);
}
文件上传处理后,返回一个通用的CommonResult 的结果对象,也方便我们在JS客户端进行判断处理。
而其中检查我们导入Excel的数据是否满足列要求的处理,就是判断它的数据列和我们预先设置好的列名是否一致即可。
//导入或导出的字段列表 string columnString = "姓名,手机,邮箱,主页,兴趣爱好,性别,年龄,出生日期,身高,备注";/// <summary> ///检查Excel文件的字段是否包含了必须的字段/// </summary> /// <param name="guid">附件的GUID</param> /// <returns></returns> public ActionResult CheckExcelColumns(stringguid)
{
CommonResult result= newCommonResult();try{
DataTable dt=ConvertExcelFileToTable(guid);if (dt != null)
{//检查列表是否包含必须的字段 result.Success =DataTableHelper.ContainAllColumns(dt, columnString);
}
}catch(Exception ex)
{
LogTextHelper.Error(ex);
result.ErrorMessage=ex.Message;
}returnToJsonContent(result);
}
而GetExcelData则是格式化Excel数据到具体的List<TestUserInfo>集合里面,这样我们方便在客户端进行各种属性的操作,它的代码如下所示。
/// <summary> ///获取服务器上的Excel文件,并把它转换为实体列表返回给客户端/// </summary> /// <param name="guid">附件的GUID</param> /// <returns></returns> public ActionResult GetExcelData(stringguid)
{if (string.IsNullOrEmpty(guid))
{return null;
}
List<TestUserInfo> list = new List<TestUserInfo>();
DataTable table=ConvertExcelFileToTable(guid);if (table != null)
{#region 数据转换 int i = 1;foreach (DataRow dr intable.Rows)
{bool converted = false;
DateTime dtDefault= Convert.ToDateTime("1900-01-01");
DateTime dt;
TestUserInfo info= newTestUserInfo();
info.Name= dr["姓名"].ToString();
info.Mobile= dr["手机"].ToString();
info.Email= dr["邮箱"].ToString();
info.Homepage= dr["主页"].ToString();
info.Hobby= dr["兴趣爱好"].ToString();
info.Gender= dr["性别"].ToString();
info.Age= dr["年龄"].ToString().ToInt32();
converted= DateTime.TryParse(dr["出生日期"].ToString(), outdt);if (converted && dt >dtDefault)
{
info.BirthDate=dt;
}
info.Height= dr["身高"].ToString().ToDecimal();
info.Note= dr["备注"].ToString();
info.Creator=CurrentUser.ID.ToString();
info.CreateTime=DateTime.Now;
info.Editor=CurrentUser.ID.ToString();
info.EditTime=DateTime.Now;
list.Add(info);
}#endregion}var result = new { total = list.Count, rows =list };returnToJsonContent(result);
}
另一个SaveExcelData的函数就是处理数据导入的最终处理函数,主要就是把集合写入到具体的数据库里面即可,具体代码如下所示。
/// <summary> ///保存客户端上传的相关数据列表/// </summary> /// <param name="list">数据列表</param> /// <returns></returns> public ActionResult SaveExcelData(List<TestUserInfo>list)
{
CommonResult result= newCommonResult();if (list != null && list.Count > 0)
{#region 采用事务进行数据提交DbTransaction trans= BLLFactory<TestUser>.Instance.CreateTransaction();if (trans != null)
{try{//int seq = 1; foreach (TestUserInfo detail inlist)
{//detail.Seq = seq++;//增加1 detail.CreateTime =DateTime.Now;
detail.Creator=CurrentUser.ID.ToString();
detail.Editor=CurrentUser.ID.ToString();
detail.EditTime=DateTime.Now;
BLLFactory<TestUser>.Instance.Insert(detail, trans);
}
trans.Commit();
result.Success= true;
}catch(Exception ex)
{
LogTextHelper.Error(ex);
result.ErrorMessage=ex.Message;
trans.Rollback();
}
}#endregion}else{
result.ErrorMessage= "导入信息不能为空";
}returnToJsonContent(result);
}
上面这几个函数的代码一般是比较有规律的,不需要一个个去编写,一般通过代码生成工具Database2Sharp批量生成即可。这样可以有效提高Web的界面代码和后台代码的开发效率,减少出错的机会。
整个导入Excel数据的处理过程,所有代码都贴出来了,基本上整个逻辑了解了就可以很好的了解这个过程的代码了。