MyException - 我的异常网
当前位置:我的异常网» ASP.NET » 组合bootstrap fileinput插件和Bootstrap-table表格

组合bootstrap fileinput插件和Bootstrap-table表格插件,实现文件上传、预览、提交的导入Excel数据操作流程

www.MyException.Cn  网友分享于:2013-09-07  浏览:0次
结合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插件声明,主要是基础的界面代码

<input id="excelFile" type="file"> 

Excel导入的的界面展示如下所示。

选择指定文件后,我们可以看到Excel的文件列表,如下界面所示。

上传文件后,数据直接展示在弹出层的列表里面,这里直接使用了 Bootstrap-table表格插件进行展示。

这样我们就可以把Excel的记录展示出来,实现了预览的功能,勾选必要的记录,然后保存即可提交到服务器进行保存,实现了Excel数据的真正导入数据库处理。

 

2、Excel导出操作详细介绍

我们在实际导入Excel的界面中,HTML代码如下所示。

<!--导入数据操作层-->
<div id="import" class="modal fade bs-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <div class="modal-header bg-primary">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true"></button>
                <h4 class="modal-title">文件导入</h4>
            </div>
            <div class="modal-body">
                <div style="text-align:right;padding:5px">
                    <a href="~/Content/Template/TestUser-模板.xls" onclick="javascript:Preview();">
                        <img alt="测试用户信息-模板" src="~/Content/images/ico_excel.png" />
                        <span style="font-size:larger;font-weight:200;color:red">TestUser-模板.xls</span>
                    </a>
                </div>
                <hr/>
                <form id="ffImport" method="post">
                    <div title="Excel导入操作" style="padding: 5px">
                        <input type="hidden" id="AttachGUID" name="AttachGUID" /> 
                        <input id="excelFile" type="file"> 
                    </div>
                </form>

                <!--数据显示表格-->
                <table id="gridImport" class="table table-striped table-bordered table-hover" cellpadding="0" cellspacing="0" border="0">
                </table>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
                <button type="button" class="btn btn-primary" onclick="SaveImport()">保存</button>
            </div>
        </div>
    </div>
</div>

对于bootstrap fileinput的各种属性,我们这里使用JS进行初始化,这样方便统一管理和修改。

        //初始化Excel导入的文件
        function InitExcelFile() {
            //记录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的值,并清空文件
        function RefreshExcel() {
            $("#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;
        function InitImport(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, string folder)
        {
            CommonResult result = new CommonResult();

            HttpFileCollectionBase files = HttpContext.Request.Files;
            if (files != null)
            {
                foreach (string key in files.Keys)
                {
                    try
                    {
                        #region MyRegion
                        HttpPostedFileBase 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 = new FileUploadInfo();
                            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对象为空";
            }

            return ToJsonContent(result);
        }

文件上传处理后,返回一个通用的CommonResult 的结果对象,也方便我们在JS客户端进行判断处理。

而其中检查我们导入Excel的数据是否满足列要求的处理,就是判断它的数据列和我们预先设置好的列名是否一致即可。

        //导入或导出的字段列表   
        string columnString = "姓名,手机,邮箱,主页,兴趣爱好,性别,年龄,出生日期,身高,备注";

        /// <summary>
        /// 检查Excel文件的字段是否包含了必须的字段
        /// </summary>
        /// <param name="guid">附件的GUID</param>
        /// <returns></returns>
        public ActionResult CheckExcelColumns(string guid)
        {
            CommonResult result = new CommonResult();

            try
            {
                DataTable dt = ConvertExcelFileToTable(guid);
                if (dt != null)
                {
                    //检查列表是否包含必须的字段
                    result.Success = DataTableHelper.ContainAllColumns(dt, columnString);
                }
            }
            catch (Exception ex)
            {
                LogTextHelper.Error(ex);
                result.ErrorMessage = ex.Message;
            }

            return ToJsonContent(result);
        }

而GetExcelData则是格式化Excel数据到具体的List<TestUserInfo>集合里面,这样我们方便在客户端进行各种属性的操作,它的代码如下所示。

        /// <summary>
        /// 获取服务器上的Excel文件,并把它转换为实体列表返回给客户端
        /// </summary>
        /// <param name="guid">附件的GUID</param>
        /// <returns></returns>
        public ActionResult GetExcelData(string guid)
        {
            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 in table.Rows)
                {
                    bool converted = false;
                    DateTime dtDefault = Convert.ToDateTime("1900-01-01");
                    DateTime dt;
                    TestUserInfo info = new TestUserInfo();

                    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(), out dt);
                    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 };
            return ToJsonContent(result);
        }

另一个SaveExcelData的函数就是处理数据导入的最终处理函数,主要就是把集合写入到具体的数据库里面即可,具体代码如下所示。

        /// <summary>
        /// 保存客户端上传的相关数据列表
        /// </summary>
        /// <param name="list">数据列表</param>
        /// <returns></returns>
        public ActionResult SaveExcelData(List<TestUserInfo> list)
        {
            CommonResult result = new CommonResult();
            if (list != null && list.Count > 0)
            {
                #region 采用事务进行数据提交

                DbTransaction trans = BLLFactory<TestUser>.Instance.CreateTransaction();
                if (trans != null)
                {
                    try
                    {
                        //int seq = 1;
                        foreach (TestUserInfo detail in list)
                        {
                            //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 = "导入信息不能为空";
            }

            return ToJsonContent(result);
        }

上面这几个函数的代码一般是比较有规律的,不需要一个个去编写,一般通过代码生成工具Database2Sharp批量生成即可。这样可以有效提高Web的界面代码和后台代码的开发效率,减少出错的机会。

整个导入Excel数据的处理过程,所有代码都贴出来了,基本上整个逻辑了解了就可以很好的了解这个过程的代码了。

2楼Kinns丶Wang
怎么保存导数据呢?
1楼名字怪难起
上次就是借鉴你的文件上传案例实现了功能。这次文章实现的功能更强大先mark了

文章评论

老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
漫画:程序员的工作
漫画:程序员的工作
Java程序员必看电影
Java程序员必看电影
代码女神横空出世
代码女神横空出世
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
一个程序员的时间管理
一个程序员的时间管理
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
我是如何打败拖延症的
我是如何打败拖延症的
旅行,写作,编程
旅行,写作,编程
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
10个调试和排错的小建议
10个调试和排错的小建议
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
程序员的鄙视链
程序员的鄙视链
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
如何成为一名黑客
如何成为一名黑客
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
编程语言是女人
编程语言是女人
我的丈夫是个程序员
我的丈夫是个程序员
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
程序员应该关注的一些事儿
程序员应该关注的一些事儿
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
鲜为人知的编程真相
鲜为人知的编程真相
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
为什么程序员都是夜猫子
为什么程序员都是夜猫子
程序员必看的十大电影
程序员必看的十大电影
程序员都该阅读的书
程序员都该阅读的书
每天工作4小时的程序员
每天工作4小时的程序员
程序员和编码员之间的区别
程序员和编码员之间的区别
总结2014中国互联网十大段子
总结2014中国互联网十大段子
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
 程序员的样子
程序员的样子
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有