---
# System prepended metadata

title: 批次 + 檔案匯入
tags: [國興實習]

---

---
tags: 國興實習
---
# 批次 + 檔案匯入

### 前端html

#### 自訂匯入按鈕+匯入介面視窗設計

```htmlmixed=
@section RightNavBtn{
    <!--------自訂匯入按鈕------------------------>
    <div class="btn-section col-md-1 col-sm-2 col-xs-2" ng-show="!isMobile">
        <button type="button" id="btnimport" class="btn btn-main" ng-disabled="status == '2'" data-toggle="modal" data-target="#importModal"><i class="glyphicon glyphicon-floppy-open"></i>匯入</button>
    </div>
    <!--------匯入excel-------------------------------------------------->
    <div class="modal fade" id="importModal" tabindex="-1" role="dialog" aria-labelledby="importModalLabel" aria-hidden="true">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                    <h5 class="modal-title" id="exampleModalLabel">選擇檔案(限定副檔名.xlsx)</h5>
                </div>

                <div class="modal-body">
                    <div class="form-group">
                        <label class="col-sm-3 control-label required-label" style="text-align:right;">檔名</label>
                        <div class="input-group col-xs-6 col-xs-offset-3">
                            <label class="input-group-btn">
                                <span class="btn btn-primary">
                                    選擇檔案<input name="stxtD_FILENAME1" type="file" file-model="FILE_NAME" style="display: none;" class="form-control input-file">
                                </span> @*//type="file"：輸入檔案模式*@
                            </label>
                            <input name="show_FILE_NAME" type="text" class="form-control" readonly>
                        </div>
                        <div class="input-group col-xs-6 col-xs-offset-3">
                            <a href="~/zTemplate/GOOGRP040.xlsx">匯入範例下載</a>
                        </div>
                    </div>

                </div>
                <div class="form-group">
                    <div class="modal-footer">
                        <button type="button" class="btn btn-primary" ng-click="doMasterUpload()"><span class="glyphicon glyphicon-ok"></span>確定</button>
                        <button type="button" class="btn btn-default" ng-click="doMasterUploadCancel()" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span>取消</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
}
```
:::info
* **直接用的話就更改`<a href="~/zTemplate/GOOGRP040.xlsx">匯入範例下載</a>`的路徑就好**
:::

### js

```javascript=
$scope.$on("masteruploading", function (e, a_arg) {
    if (isEmpty($scope.FILE_NAME)) {
        swal("請選擇檔案以供上傳!!", "", "error");
        a_arg.cancel = true;
    }
    var FILE_EXT = $scope.FILE_NAME.name.split('.');
    if (FILE_EXT[FILE_EXT.length - 1] != "xlsx") {
        swal("副檔名非xlsx，請檢查檔案!!", "", "error");
        a_arg.cancel = true;
    }
    //清空回傳序號
    $scope.searchData.FILE_NAME = $scope.FILE_NAME.name.split(".xlsx")[0];
    $scope.searchData.FILE_EXT = $scope.FILE_NAME.name.split('.')[FILE_EXT.length - 1];
    a_arg.files.push($scope.FILE_NAME);
})

//--取得[上傳檔案]結果 uploaded --------------------------------------------------------
$scope.$on("masteruploaded", function (e, a_arg) {
    //取得[上傳檔案]名稱
    //a_arg.message = "上傳作業完成！" + a_arg.files[0];
    a_arg.isShowMessage = false;//不顯示成功訊息
    $scope.searchData.UPLOAD_NAME = a_arg.files[0];

    //$scope.searchData.FILE_EXT = $scope.lbFILEEXT;
    $scope.doImpMasterXLS();//執行完upload後開始執行讀取檔案
    //將名稱清空，避免重複上傳
})
//--匯入excel結果 impMasterXLSed ----------------------------------------
$scope.$on("impMasterXLSed", function (e, a_arg) {
    a_arg.isShowMessage = false;
    //swal("已成功匯入！" + a_arg.result.msg, "", "success")
    if (a_arg.result["result"] == "0") {
        swal(a_arg.result["msg"], "", "success").then(function () {
            $("#importModal").modal("hide");
            $scope.doMasterUploadCancel();
            $scope.onBtnSearchClick();
        });
    }
    else {
        swal(a_arg.result["msg"], "", "warning");
    }

});

$scope.doMasterUploadCancel = function () {
    if (!isEmpty($scope.FILE_NAME)) {
        $("input[name='show_FILE_NAME']").val('');
        $("input[type='file']").val("");
    }
}

$(function () { //秀出檔案名
    $("input[type='file']").bind("change", function (event) {
        if (event.target.files.length > 0) {
            var file = event.target.files[0];
            $("input[name='show_FILE_NAME']").val(file.name);
        }
        else {
            $("input[name='show_FILE_NAME']").val('');
        }
    });
});
```
:::info
* **直接用的話不用更改**
:::

### 後端
```C#=
[System.Web.Http.HttpPost]
        public IHttpActionResult ImpMasterXls(SearchClass a_Search)
        {
            string userId = UserInfo.getUserInfo(HttpContext.Current.Session).PASS_NO; //取得使用者ID
            string userIp = HttpContext.Current.Request.UserHostAddress; //取得使用者IP
            DateTime now = DateTime.Now; //取得目前日期時間

            #region 上傳檔案
            string l_uploadname = a_Search.UPLOAD_NAME;//上傳檔案產生的檔名
            string l_filename = a_Search.FILE_NAME;//檔案名稱
            string l_fileext = a_Search.FILE_EXT;//副檔名
            string l_root = HttpContext.Current.Server.MapPath("~/zTmpUp/");//原始路徑
            string l_newpath = HttpContext.Current.Server.MapPath("~/FILES/GOO/");//要存入的實際路徑
            string l_newfilepath = l_newpath + l_filename + "." + l_fileext;

            //若無此路徑，則建立
            if (!System.IO.Directory.Exists(l_newpath))
            {
                System.IO.Directory.CreateDirectory(l_newpath);
            }
            //若已存在檔案，則刪除
            if (System.IO.File.Exists(l_newfilepath))
            {
                try
                {
                    System.IO.File.Delete(l_newfilepath);
                }
                catch (System.IO.IOException e)
                {
                    Console.WriteLine(e.Message);
                }
            }
            //移動檔案並補上副檔名
            System.IO.File.Move(l_root + l_uploadname, l_newfilepath);
            // 這裡開始匯入處理

            #endregion

            DBController l_dbc = new DBController("UseDB"); //與資料庫連線
            DataSet ds = new DataSet(); 
            Hashtable l_ht = new Hashtable(); //存取資料
            StringBuilder l_SQL = new StringBuilder(); //SQL指令

            //回傳匯入結果
            Dictionary<string, object> l_dic = new Dictionary<string, object>();

            //顯示用
            int totalrow = 0;

            bool l_Succ = false;
            string l_Msg = "";
            string ErrMsg = "";//紀錄錯誤訊息

            //批次存檔用
            DeltaMD deltamd = new DeltaMD();
            deltamd.details = new DeltaDetail[] { };
            deltamd.masterRows = new DeltaRow[] { };
            deltamd.masterRow = new DeltaRow();
            List<DeltaRow> listDetail_unit = new List<DeltaRow>();

            string l_data_na = '\\' + l_filename + "." + l_fileext;

            using (FileStream fs = new FileStream(l_newpath + l_data_na, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                //成功開啟excel
                //載入Excel檔案
                using (ExcelPackage ep = new ExcelPackage(fs))
                {
                    ExcelWorksheet sheet = ep.Workbook.Worksheets[1];//取得Sheet1
                    int startRowNumber = sheet.Dimension.Start.Row;//起始列編號，從1算起
                    int endRowNumber = sheet.Dimension.End.Row;//結束列編號，從1算起
                    int startColumn = sheet.Dimension.Start.Column;//開始欄編號，從1算起
                    int endColumn = sheet.Dimension.End.Column;//結束欄編號，從1算起
                    startRowNumber = 2;//第一列為標題
                    totalrow = endRowNumber;

                    //讀取資料  
                    #region  判斷標題欄位位置
                    string col_na = ""; //供switch case尋找欄位名稱
                    int CUST_DEPT = -1; //紀錄相關欄位欄號之變數
                    int CUST_NO = -1;
                    int UNIT_NO = -1;
                    int UNIT_NM = -1;
                    int EMP_NO = -1;
                    int EMP_TEL = -1;

                    #endregion

                    #region  判斷標題欄位位置
                    for (int i = 1; i >= startColumn && i <= endColumn; i++)
                    {
                        if (sheet.Cells[1, i].Value != null) //取得標題列，且不為空值
                        {
                            col_na = (sheet.Cells[1, i].Value).ToString().Replace(" ", "").Trim(); //Replace及Trim都是在做去除空白的處理動作
                        }
                        else
                        {
                            col_na = ""; //將空值定義為空字串
                        }

                        switch (col_na) //辨別標題欄位並找到其"欄"位的欄號
                        {
                            case "部門代號":
                                CUST_DEPT = i;
                                break;
                            case "客戶代號":
                                CUST_NO = i;
                                break;
                            case "單位代碼":
                                UNIT_NO = i;
                                break;
                            case "單位名稱":
                                UNIT_NM = i;
                                break;
                            case "承辦人":
                                EMP_NO = i;
                                break;
                            case "電話":
                                EMP_TEL = i;
                                break;
                        }
                    }
                    #endregion

                    string cellValue_CUST_DEPT = ""; //用來抓取目前列之各欄資料
                    string cellValue_CUST_NO = "";
                    string cellValue_UNIT_NO = "";
                    string cellValue_UNIT_NM = "";
                    string cellValue_EMP_NO = "";
                    string cellValue_EMP_TEL = "";


                    for (int currentRow = startRowNumber; currentRow <= endRowNumber + 1; currentRow++)
                    {
                        ExcelRange range = sheet.Cells[currentRow, startColumn, currentRow, endColumn];//抓出目前的Excel列
                        if (range.Any(c => !string.IsNullOrEmpty(c.Text)) == false)//這是一個完全空白列(使用者用Delete鍵刪除動作)
                        {
                            continue;//略過此列
                        }

                        Dictionary<string, object> a_row = new Dictionary<string, object>();
                        #region  null判斷及空白清除
                        if (CUST_DEPT != -1)
                        {
                            cellValue_CUST_DEPT = General.nvl(sheet.Cells[currentRow, CUST_DEPT].Value, "").ToString();
                            cellValue_CUST_DEPT = cellValue_CUST_DEPT.Trim(); // 清除左右空白
                        }
                        if (CUST_NO != -1)
                        {
                            cellValue_CUST_NO = General.nvl(sheet.Cells[currentRow, CUST_NO].Value, "").ToString();
                            cellValue_CUST_NO = cellValue_CUST_NO.Trim(); // 清除左右空白
                        }
                        if (UNIT_NO != -1)
                        {
                            cellValue_UNIT_NO = General.nvl(sheet.Cells[currentRow, UNIT_NO].Value, "").ToString();
                            cellValue_UNIT_NO = cellValue_UNIT_NO.Trim(); // 清除左右空白
                        }
                        if (UNIT_NM != -1)
                        {
                            cellValue_UNIT_NM = General.nvl(sheet.Cells[currentRow, UNIT_NM].Value, "").ToString();
                            cellValue_UNIT_NM = cellValue_UNIT_NM.Trim(); // 清除左右空白
                        }
                        if (EMP_NO != -1)
                        {
                            cellValue_EMP_NO = General.nvl(sheet.Cells[currentRow, EMP_NO].Value, "").ToString();
                            cellValue_EMP_NO = cellValue_EMP_NO.Trim(); // 清除左右空白
                        }
                        if (EMP_TEL != -1)
                        {
                            cellValue_EMP_TEL = General.nvl(sheet.Cells[currentRow, EMP_TEL].Value, "").ToString();
                            cellValue_EMP_TEL = cellValue_EMP_TEL.Trim(); // 清除左右空白
                        }
                        #endregion

                        #region 欄位驗證

                        //Regex regex = new Regex(string pattern);
                        //Regex regex = new Regex(string pattern, RegexOptions options);

                        //最多999筆,超過1000要防呆
                        if (currentRow > 1000)
                        {
                            ErrMsg += "匯入筆數最多只可為999筆，超過999筆系統無法儲存<br>";
                            l_Succ = (ErrMsg != "") ? false : true;
                            return doImportOKResult(l_Succ, ErrMsg, l_dic);
                        }

                        //以下依資料庫規格更改if條件，以協助客戶更改資料規格
                        if (cellValue_CUST_DEPT == "")
                        {
                            ErrMsg += "列號：" + currentRow + ",[部門代號]不可空白<br>";
                        }
                        else if (cellValue_CUST_DEPT.Length > 10)
                        {
                            ErrMsg += "列號：" + currentRow + ",[部門代號]不可超過20碼<br>";
                        }
                        if (cellValue_CUST_NO == "")
                        {
                            ErrMsg += "列號：" + currentRow + ",[客戶代號]不可空白<br>";
                        }
                        else if (cellValue_CUST_NO.Length > 20)
                        {
                            ErrMsg += "列號：" + currentRow + ",[客戶代號]不可超過20碼<br>";
                        }

                        if (cellValue_UNIT_NO == "")
                        {
                            ErrMsg += "列號：" + currentRow + ",[單位代碼]不可空白<br>";
                        }
                        else if (cellValue_UNIT_NO.Length > 20)
                        {
                            ErrMsg += "列號：" + currentRow + ",[單位代碼]不可超過20碼<br>";
                        }

                        if (cellValue_UNIT_NM == "")
                        {
                            ErrMsg += "列號：" + currentRow + ",[單位名稱]不可空白<br>";
                        }
                        else if (cellValue_UNIT_NM.Length > 40)
                        {
                            ErrMsg += "列號：" + currentRow + ",[單位名稱]不可超過40碼<br>";
                        }

                        if (cellValue_EMP_NO == "")
                        {
                            ErrMsg += "列號：" + currentRow + ",[承辦人]不可空白<br>";
                        }
                        else if (cellValue_EMP_NO.Length > 20)
                        {
                            ErrMsg += "列號：" + currentRow + ",[承辦人]不可超過20碼<br>";
                        }

                        if (cellValue_EMP_TEL == "")
                        {
                            ErrMsg += "列號：" + currentRow + ",[電話]不可空白<br>";
                        }
                        else if (!Regex.Match(cellValue_EMP_TEL, "^[0-9]").Success) //正規表示法判斷是否只輸入數字
                        {
                            ErrMsg += "列號：" + currentRow + ",[電話]非數字<br>";
                        }
                        else if (cellValue_EMP_TEL.Length > 20)
                        {
                            ErrMsg += "列號：" + currentRow + ",[電話]不可超過20碼<br>";
                        }

                        #endregion

                        #region 存檔用 欄位
                        DeltaRow temp_unit = new DeltaRow();
                        temp_unit.newRow = new Dictionary<string, object>();
                        temp_unit.newRow.Add("CUST_DEPT", cellValue_CUST_DEPT);
                        temp_unit.newRow.Add("CUST_NO", cellValue_CUST_NO);
                        temp_unit.newRow.Add("UNIT_NO", cellValue_UNIT_NO);
                        temp_unit.newRow.Add("NAME", cellValue_UNIT_NM);
                        temp_unit.newRow.Add("EMP_NO", cellValue_EMP_NO);
                        temp_unit.newRow.Add("TEL", "0" + cellValue_EMP_TEL);
                        temp_unit.newRow.Add("CRT_NO", userId);
                        temp_unit.newRow.Add("CRT_DATE", now);
                        temp_unit.newRow.Add("CRT_IP", userIp);
                        temp_unit.newRow.Add("UPD_NO", userId);
                        temp_unit.newRow.Add("UPD_DATE", now);
                        temp_unit.newRow.Add("UPD_IP", userIp);

                        listDetail_unit.Add(temp_unit);
                        #endregion
                    }

                    l_Msg = ErrMsg;
                    l_Succ = string.IsNullOrEmpty(ErrMsg) ? true : false;
                }
            }

            //批次存檔
            if (ErrMsg == "")
            {
                List<Hashtable> batchHt = new List<Hashtable>();
                List<string> batchSQL = new List<string>();
                int count = 0;

                try
                {
                    if (listDetail_unit.Count > 0)
                    {
                        string _sql = @"INSERT INTO QA_CUST_UNIT (CUST_DEPT, CUST_NO, UNIT_NO, NAME, EMP_NO, TEL, CRT_NO, CRT_DATE, CRT_IP, UPD_NO, UPD_DATE, UPD_IP) 
                                    VALUES " + "({0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11})"; //這裡的VALUES對應到【存檔用 欄位】輸入的順序，要特別注意

                        l_ht.Clear();

                        foreach (DeltaRow row in listDetail_unit)
                        {
                            #region 設定帶入SQL值 
                            string pCUST_DEPT = "@CUST_DEPT_" + count;  //+count是為了避免批次時l_SQL的@參數重複
                            string pCUST_NO = "@CUST_NO_" + count;
                            string pUNIT_NO = "@UNIT_NO_" + count;
                            string pNAME = "@NAME_" + count;
                            string pEMP_NO = "@EMP_NO_" + count;
                            string pTEL = "@TEL_" + count;
                            string pCRT_NO = "@CRT_NO_" + count;
                            string pCRT_DATE = "@CRT_DATE_" + count;
                            string pCRT_IP = "@CRT_IP_" + count;
                            string pUPD_NO = "@UPD_NO_" + count;
                            string pUPD_DATE = "@UPD_DATE_" + count;
                            string pUPD_IP = "@UPD_IP_" + count;
                            #endregion

                            l_SQL.AppendLine(string.Format(_sql, pCUST_DEPT, pCUST_NO, pUNIT_NO, pNAME, pEMP_NO, pTEL, pCRT_NO, pCRT_DATE, pCRT_IP, pUPD_NO, pUPD_DATE, pUPD_IP));

                            #region 設定hashtable
                            l_ht.Add(pCUST_DEPT, new StructureSQLParameter(row.newRow["CUST_DEPT"], SqlDbType.NVarChar));
                            l_ht.Add(pCUST_NO, new StructureSQLParameter(row.newRow["CUST_NO"], SqlDbType.NVarChar));
                            l_ht.Add(pUNIT_NO, new StructureSQLParameter(row.newRow["UNIT_NO"], SqlDbType.NVarChar));
                            l_ht.Add(pNAME, new StructureSQLParameter(row.newRow["NAME"], SqlDbType.NVarChar));
                            l_ht.Add(pEMP_NO, new StructureSQLParameter(row.newRow["EMP_NO"], SqlDbType.NVarChar));
                            l_ht.Add(pTEL, new StructureSQLParameter(row.newRow["TEL"], SqlDbType.NVarChar));
                            l_ht.Add(pCRT_NO, new StructureSQLParameter(row.newRow["CRT_NO"], SqlDbType.NVarChar));
                            l_ht.Add(pCRT_DATE, new StructureSQLParameter(row.newRow["CRT_DATE"], SqlDbType.DateTime));
                            l_ht.Add(pCRT_IP, new StructureSQLParameter(row.newRow["CRT_IP"], SqlDbType.NVarChar));
                            l_ht.Add(pUPD_NO, new StructureSQLParameter(row.newRow["UPD_NO"], SqlDbType.NVarChar));
                            l_ht.Add(pUPD_DATE, new StructureSQLParameter(row.newRow["UPD_DATE"], SqlDbType.DateTime));
                            l_ht.Add(pUPD_IP, new StructureSQLParameter(row.newRow["UPD_IP"], SqlDbType.NVarChar));
                            #endregion

                            //避免參數過多造成例外錯誤發生
                            if (l_ht.Count > 1800)
                            {
                                //加入批次執行List
                                batchHt.Add(new Hashtable(l_ht));
                                batchSQL.Add(_sql.ToString());
                                //清空此階段的Hashtable與StringBuilder
                                l_ht.Clear();
                                l_SQL.Clear();
                            }
                            count++;
                        }
                        //加入最後一次
                        if (l_ht.Count > 0)
                        {
                            batchHt.Add(new Hashtable(l_ht));
                            batchSQL.Add(l_SQL.ToString());
                            l_ht.Clear();
                            l_SQL.Clear();
                        }

                        //------批次執行List SQL------

                        DbConnection conn = l_dbc.NewConnection();
                        DbTransaction trans = conn.BeginTransaction();
                        string message = "";

                        try
                        {
                            for (var i = 0; i < batchSQL.Count; i++)
                            {
                                l_dbc.DbExecuteNonQuery(batchSQL[i], batchHt[i], conn, trans);
                            }
                            trans.Commit();
                        }
                        catch (Exception ex)
                        {
                            trans.Rollback();
                            message = ex.ToString();
                        }
                        finally
                        {
                            trans.Dispose();
                            conn.Close();
                        }

                        if (l_Succ)
                        {
                            if (message == "")
                            {
                                l_dic["result"] = "0";
                                l_dic["msg"] = "共匯入" + listDetail_unit.Count + "筆資料";
                                return new OkNegotiatedContentResult<Dictionary<string, object>>(l_dic, this);
                            }
                            else
                            {
                                l_dic["result"] = "1";
                                l_dic["msg"] = message;
                                return new OkNegotiatedContentResult<Dictionary<string, object>>(l_dic, this);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    l_dic["result"] = "1";
                    l_dic["msg"] = ex.Message;
                    return new OkNegotiatedContentResult<Dictionary<string, object>>(l_dic, this);
                }
            }

            return doImportOKResult(l_Succ, l_Msg, l_dic);
        }
```
:::info
* **依註解慢慢更改欄位（多利用Ctrl+H）**
* **批次存檔就只是把SQL指令全串一起後再一次發送**
* **UserInfo右鍵查看各變數定義，依需求使用**
:::