基于MVC+EasyUI的Web開發(fā)框架經(jīng)驗(yàn)總結(jié)(10)--在Web界面上實(shí)現(xiàn)數(shù)據(jù)的導(dǎo)入和導(dǎo)出

數(shù)據(jù)的導(dǎo)入導(dǎo)出堤结,在很多系統(tǒng)里面都比較常見,這個(gè)導(dǎo)入導(dǎo)出的操作煌贴,在Winform里面比較容易實(shí)現(xiàn)蜻展,我曾經(jīng)在之前的一篇文章《Winform開發(fā)框架之通用數(shù)據(jù)導(dǎo)入導(dǎo)出操作》介紹了在Winform里面的通用導(dǎo)入導(dǎo)出模塊的設(shè)計(jì)和開發(fā)過程,但在Web上我們應(yīng)該如何實(shí)現(xiàn)呢渔隶?本文主要介紹利用MVC4+EasyUI的特點(diǎn)羔挡,并結(jié)合文件上傳控件Uploadify 的使用,實(shí)現(xiàn)文件上傳后馬上進(jìn)行處理并顯示间唉,然后確認(rèn)后把數(shù)據(jù)寫入數(shù)據(jù)庫的過程绞灼。
我們知道,Web上對Excel的處理和Winform的有所差異呈野,如果是在Web上處理低矮,我們需要把Excel文檔上傳到服務(wù)器上,然后讀取文件進(jìn)行顯示被冒,所以第一步是實(shí)現(xiàn)文件的上傳操作军掂,關(guān)于文件上傳控件,具體可以參考我的文章《基于MVC4+EasyUI的Web開發(fā)框架形成之旅--附件上傳組件uploadify的使用》昨悼。

1蝗锥、導(dǎo)入數(shù)據(jù)的界面效果展示

在Winform里面,我們處理Excel數(shù)據(jù)導(dǎo)入的界面如下所示率触。



在Web上的主界面如下所示终议。



導(dǎo)入界面如下所示。

2葱蝗、Web數(shù)據(jù)導(dǎo)入的處理邏輯和代碼

為了實(shí)現(xiàn)Web上的數(shù)據(jù)導(dǎo)入導(dǎo)出操作痊剖,我們需要增加兩個(gè)按鈕,一個(gè)是導(dǎo)入按鈕垒玲,一個(gè)是導(dǎo)出按鈕。

 <a href="javascript:void(0)" class="easyui-linkbutton" id="btnImport" iconcls="icon-excel" onclick="ShowImport()">導(dǎo)入</a>
 <a href="javascript:void(0)" class="easyui-linkbutton" id="btnExport" iconcls="icon-excel" onclick="ShowExport()">導(dǎo)出</a>

導(dǎo)入的JS處理代碼如下所示找颓。

//顯示導(dǎo)入界面
function ShowImport() {
    $.showWindow({
        title: '客戶聯(lián)系人-Excel數(shù)據(jù)導(dǎo)入',
        useiframe: true,
        width: 1024,
        height: 700,
        content: 'url:/Contact/Import',
        buttons: [{
            text: '取消',
            iconCls: 'icon-cancel',
            handler: function (win) {
                win.close();
            }
        }]
    });
}

上面主要就是彈出一個(gè)窗口(上面的導(dǎo)入數(shù)據(jù)窗口)合愈,用來方便客戶選擇Excel文件并保存數(shù)據(jù)或者下載導(dǎo)入模板等操作的。

然后在Import.cshtml的視圖代碼里面,我們需要初始化Datagrid和相關(guān)的界面元素佛析,初始化DataGrid的代碼如下所示益老。

//實(shí)現(xiàn)對DataGird控件的綁定操作
function InitGrid() {
    var guid = $("#AttachGUID").val();
    $('#grid').datagrid({   //定位到Table標(biāo)簽,Table標(biāo)簽的ID是grid
        url: '/Contact/GetExcelData?guid=' + guid,   //指向后臺的Action來獲取當(dāng)前用戶的信息的Json格式的數(shù)據(jù)
        title: '客戶聯(lián)系人-Excel數(shù)據(jù)導(dǎo)入',
        iconCls: 'icon-view',
        height: 400,
        width: function () { return document.body.clientWidth * 0.9 },//自動寬度

        ..................

上面紅色部分的內(nèi)容寸莫,就是我們在文件順利上傳到服務(wù)器上的時(shí)候捺萌,根據(jù)一個(gè)guid的參數(shù)初始化DataGrid的列表數(shù)據(jù)。

下面是附件上傳控件uploadify的初始化腳本代碼膘茎,其中紅色部分注意一下桃纯,我們需要上傳的是一個(gè)文件,并且不允許多選披坏,限定上傳文件的類型為xls态坦。

文件上傳完成后,首先調(diào)用CheckExcelColumns控制器函數(shù)來檢查是否匹配導(dǎo)入模板的字段棒拂,如果匹配通過伞梯,加載Excel并展示數(shù)據(jù)到Datagrid里面,否則提示用戶按模板格式錄入數(shù)據(jù)帚屉。

<script type="text/javascript">
$(function () {
    //添加界面的附件管理
    $('#file_upload').uploadify({
        'swf': '/Content/JQueryTools/uploadify/uploadify.swf',  //FLash文件路徑
        'buttonText': '瀏  覽',                                 //按鈕文本
        'uploader': '/FileUpload/Upload',                       //處理ASHX頁面
        'queueID': 'fileQueue',                        //隊(duì)列的ID
        'queueSizeLimit': 1,                          //隊(duì)列最多可上傳文件數(shù)量谜诫,默認(rèn)為999
        'auto': false,                                 //選擇文件后是否自動上傳,默認(rèn)為true
        'multi': false,                                 //是否為多選攻旦,默認(rèn)為true
        'removeCompleted': true,                       //是否完成后移除序列喻旷,默認(rèn)為true
        'fileSizeLimit': '10MB',                       //單個(gè)文件大小,0為無限制敬特,可接受KB,MB,GB等單位的字符串值
        'fileTypeDesc': 'Excel Files',                 //文件描述
        'fileTypeExts': '*.xls',  //上傳的文件后綴過濾器
        'onQueueComplete': function (event, data) {    //所有隊(duì)列完成后事件
            var guid = $("#AttachGUID").val();
            ViewUpFiles(guid, "div_files");

            //提示用戶Excel格式是否正常掰邢,如果正常加載數(shù)據(jù)
            $.ajax({
                url: '/Contact/CheckExcelColumns?guid=' + guid,
                type: 'get',
                dataType:'json',
                success: function (data) {
                    if (data.Success) {                                
                        InitGrid(); //重新刷新表格數(shù)據(jù)
                        $.messager.alert("提示", "文件已上傳,數(shù)據(jù)加載完畢伟阔!");
                    }
                    else {
                        $.messager.alert("提示", "上傳的Excel文件檢查不通過辣之。請根據(jù)頁面右上角的Excel模板格式進(jìn)行數(shù)據(jù)錄入。");
                    }
                }
            });                    
        },
        'onUploadStart': function (file) {
            InitUpFile();//上傳文件前 皱炉,重置GUID怀估,每次不同
            $("#file_upload").uploadify("settings", 'formData', { 'folder': '數(shù)據(jù)導(dǎo)入文件', 'guid': $("#AttachGUID").val() }); //動態(tài)傳參數(shù)
        },
        'onUploadError': function (event, queueId, fileObj, errorObj) {
            //alert(errorObj.type + ":" + errorObj.info);
        }
    });
});

為了有效處理數(shù)據(jù)的導(dǎo)入,我們需要嚴(yán)格保證導(dǎo)入的數(shù)據(jù)是和模板的字段是匹配的合搅,否則處理容易出錯(cuò)多搀,也沒有任何意義。為了實(shí)現(xiàn)這個(gè)目的灾部,框架里面提供方法對字段進(jìn)行檢查康铭,主要是確保Excel里面包含了完整的字段即可。

/// <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);
}

而在InitGrid的初始化中的這個(gè)GetExcelData的控制器方法如下所示赌髓。主要的邏輯就是獲取到Excel从藤,并把Excel里面的數(shù)據(jù)轉(zhuǎn)換為DataTable催跪,最后初始化為實(shí)體類列表,并返回給調(diào)用頁面就可以了夷野。

/// <summary>
/// 獲取服務(wù)器上的Excel文件懊蒸,并把它轉(zhuǎn)換為實(shí)體列表返回給客戶端
/// </summary>
/// <param name="guid">附件的GUID</param>
/// <returns></returns>
public ActionResult GetExcelData(string guid)
{
    if (string.IsNullOrEmpty(guid))
    {
        return null;
    }

    List<ContactInfo> list = new List<ContactInfo>();
    DataTable table = ConvertExcelFileToTable(guid);
    if (table != null)
    {
        #region 數(shù)據(jù)轉(zhuǎn)換
        int i = 1;
        foreach (DataRow dr in table.Rows)
        {
            string customerName = dr["客戶名稱"].ToString();
            if (string.IsNullOrEmpty(customerName))
            {
                continue;//客戶名稱為空,記錄跳過
            }

            CustomerInfo customerInfo = BLLFactory<Customer>.Instance.FindByName(customerName);
            if (customerInfo == null)
            {
                continue;//客戶名稱不存在悯搔,記錄跳過
            }

            ContactInfo info = new ContactInfo();
            info.Customer_ID = customerInfo.ID;//客戶ID
            info.HandNo = dr["編號"].ToString();
            info.Name = dr["姓名"].ToString();
             ..............................//增加一個(gè)特殊字段的轉(zhuǎn)義
            info.Data1 = BLLFactory<Customer>.Instance.GetCustomerName(info.Customer_ID);

            list.Add(info);
        }
        #endregion
    }
    var result = new { total = list.Count, rows = list };
    return JsonDate(result);
}

3骑丸、Web上數(shù)據(jù)的導(dǎo)出操作

剛才介紹了數(shù)據(jù)的導(dǎo)入操作,數(shù)據(jù)的導(dǎo)出操作相對簡單一些妒貌,它的JS函數(shù)操作如下所示通危。

//導(dǎo)出Excel數(shù)據(jù)
var exportCondition;
function ShowExport() {
    var url = "/Contact/Export";
    $.ajax({
        type: "POST",
        url: url,
        data: exportCondition,
        success: function (filePath) {
            var downUrl = '/FileUpload/DownloadFile?file=' + filePath;
            window.location = downUrl;
        }
    });
}

雖然數(shù)據(jù)的導(dǎo)出比較簡單一點(diǎn),但是由于我們需要使用POST方式對數(shù)據(jù)條件進(jìn)行提交苏揣,因此不像普通的方式下載文件Window.Open(url)就可以實(shí)現(xiàn)文件下載了黄鳍。如果POST方式提交了參數(shù),那么返回的數(shù)據(jù)即使是文件流平匈,也無法進(jìn)行有效的下載框沟。

從上面的腳本我們可以看到,里面的exportCondition就是我們需要提交到服務(wù)器的條件增炭,服務(wù)器根據(jù)這個(gè)條件進(jìn)行檢索數(shù)據(jù)忍燥,并返回一個(gè)Excel文件就可以了。

由于使用ajax這種POST方式無法直接下載文件流隙姿,因此梅垄,我們需要先根據(jù)條件,在服務(wù)器上生成文件输玷,返回一個(gè)文件路徑队丝,再次通過DownloadFile方法進(jìn)行文件的下載才可以。

因此這個(gè)傳遞的條件也是很重要的欲鹏,在查詢操作的時(shí)候机久,我們可以把對應(yīng)的條件傳遞給它。

//綁定搜索按鈕的的點(diǎn)擊事件
function BindSearchEvent() {
    //按條件進(jìn)行查詢數(shù)據(jù)赔嚎,首先我們得到數(shù)據(jù)的值
    $("#btnSearch").click(function () {
        //得到用戶輸入的參數(shù)
        //取值有幾種方式:$("#id").combobox('getValue'), $("#id").datebox('getValue'), $("#id").val(),combotree('getValue')
        //字段增加WHC_前綴字符膘盖,避免傳遞如URL這樣的Request關(guān)鍵字沖突
        var queryData = {
             WHC_Name: $("#txtName").val(),
             WHC_OfficePhone: $("#txtOfficePhone").val(),
             WHC_Mobile: $("#txtMobile").val(),
             WHC_Address: $("#txtAddress").val(),
             WHC_Email: $("#txtEmail").val(),
             WHC_Note: $("#txtNote").val()
          }
        //將值傳遞給DataGrid
        InitGrid(queryData);

        //傳遞給導(dǎo)出操作
        exportCondition = queryData;

        return false;
    });
}

在我們選定某個(gè)樹的節(jié)點(diǎn)的時(shí)候,我們也可以傳遞自定義的條件給它尤误。

//根據(jù)消息分組加載指定列表
function loadByGroupTree(node) {
    //賦值給特殊字段侠畔,公司和部門查詢的時(shí)候選擇其中一個(gè)
    var queryParams = $('#grid').datagrid('options').queryParams;
    var condition = "{ id: \"" + node.id +"\", groupname:\"" + node.text +"\", userid:\"" + @Session["UserId"] + "\" }";
    queryParams.CustomedCondition = condition;//提供給datagrid的條件

    exportCondition = { CustomedCondition: condition };//提供給導(dǎo)出的條件

    $("#grid").datagrid("reload");
    $('#grid').datagrid('uncheckAll');
}

后臺的Export控制器方法主要的邏輯如下所示。



最終是返回一個(gè)生成好的文件地址损晤。
最后給一個(gè)方法直接下載文件就可以了软棺。

/// <summary>
/// 根據(jù)路徑下載文件,主要用于生成的文件的下載
/// </summary>
/// <param name="filePath">文件路徑</param>
/// <returns></returns>
public ActionResult DownloadFile(string file)
{
    string realPath = Server.MapPath(file);
    string saveFileName = FileUtil.GetFileName(realPath);

    Response.WriteFile(realPath);
    Response.Charset = "GB2312";
    Response.ContentEncoding = Encoding.GetEncoding("GB2312");
    Response.ContentType = "application/ms-excel/msword";
    Response.AppendHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(saveFileName));
    Response.Flush();
    Response.End();

    return new FileStreamResult(Response.OutputStream, "application/ms-excel/msword");
}

導(dǎo)出的Excel界面效果如下所示尤勋。



由于篇幅的原因喘落,這個(gè)導(dǎo)入導(dǎo)出的操作就介紹到這里德崭,希望有問題大家共同探討。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末揖盘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子锌奴,更是在濱河造成了極大的恐慌兽狭,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鹿蜀,死亡現(xiàn)場離奇詭異箕慧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)茴恰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門颠焦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人往枣,你說我怎么就攤上這事伐庭。” “怎么了分冈?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵圾另,是天一觀的道長。 經(jīng)常有香客問我雕沉,道長集乔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任坡椒,我火速辦了婚禮扰路,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘倔叼。我一直安慰自己汗唱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布缀雳。 她就那樣靜靜地躺著渡嚣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪肥印。 梳的紋絲不亂的頭發(fā)上识椰,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機(jī)與錄音深碱,去河邊找鬼腹鹉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛敷硅,可吹牛的內(nèi)容都是我干的功咒。 我是一名探鬼主播愉阎,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼力奋!你這毒婦竟也來了榜旦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤景殷,失蹤者是張志新(化名)和其女友劉穎溅呢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體猿挚,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咐旧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绩蜻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铣墨。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖办绝,靈堂內(nèi)的尸體忽然破棺而出伊约,到底是詐尸還是另有隱情,我是刑警寧澤八秃,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布碱妆,位于F島的核電站,受9級特大地震影響昔驱,放射性物質(zhì)發(fā)生泄漏疹尾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一骤肛、第九天 我趴在偏房一處隱蔽的房頂上張望纳本。 院中可真熱鬧,春花似錦腋颠、人聲如沸繁成。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巾腕。三九已至,卻和暖如春絮蒿,著一層夾襖步出監(jiān)牢的瞬間尊搬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工土涝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留佛寿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓但壮,卻偏偏與公主長得像冀泻,于是被迫代替她去往敵國和親常侣。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容