天天看點

Web程式導出Excel文檔

版權聲明:歡迎評論和轉載,轉載請注明來源。 https://blog.csdn.net/zy332719794/article/details/8938475

        今天接到一個任務,把資料庫裡面的資料導出到Excel文檔。系統是Web程式的,使用的是MVC架構。第一個想法是很簡單,儲存.csv檔案。做法是首先把資料讀到一個DataTable裡面,然後将資料分隔寫入檔案就OK了,最後在相應請求下載下傳檔案。

代碼如下:

/// <summary>
        /// 導出資料
        /// </summary>
        public ActionResult ExportData(int processId)
        {
            var myProcessId = processId;

            var items = db.NecessaryMaterialsDbSet.Include(p => p.ProcessItem)
                          .OrderBy(p => p.Category)
                          .ToList()
                          .Where(p => p.ProcessItem.Id == processId);

            if (!items.Any())
            {
                return RedirectToAction("Index", new { processId = myProcessId });
            }

            var dt = new DataTable();

            dt.Columns.Add("類别");
            dt.Columns.Add("材料名稱");
            dt.Columns.Add("材料來源");
            dt.Columns.Add("來源類型");
            dt.Columns.Add("原件、影印件");
            dt.Columns.Add("備注");

            foreach (var materialse in items)
            {
                dt.Rows.Add(materialse.Category,
                            materialse.Name,
                            materialse.Source,
                            materialse.SourceType,
                            materialse.OriginalOrCopy,
                            materialse.Note);
            }
            
            //生成檔案
            string fileName = items.First().ProcessItem.ProcessName + "-必備材料.csv";

            var sw = new StringWriter();
            var title = new StringBuilder();

            title.Append(dt.Columns[0].ColumnName);

            for (int i = 1; i < dt.Columns.Count; i++)
            {
                title.AppendFormat(",{0}", dt.Columns[i].ColumnName);
            }

            sw.WriteLine(title);

            var content = new StringBuilder();

            foreach (DataRow row in dt.Rows)
            {
                content.Append(row[0]);

                for (int i = 1; i < dt.Columns.Count; i++)
                {
                    content.AppendFormat(",{0}", row[i]);
                }

                sw.WriteLine(content);
                content.Clear();
            }

            sw.Close();

            Response.AddHeader("Content-Disposition", "attachment; filename=" + Server.UrlEncode(fileName));
            Response.ContentType = "vnd.ms-excel.numberformat:yyyy-MM-dd ";
            Response.ContentEncoding = System.Text.Encoding.GetEncoding("GB2312");
            Response.Write(sw);
            Response.End();
            
            return RedirectToAction("Index", new { processId = myProcessId });
        }           

但是有個問題,我需要導出有合并單元格的樣式的Excel,這樣似乎不能實作。

如下圖所示:

第一個想法是使用Office的Excel的API,有個同僚建議使用Table來構造。結果試了下,果然不錯。

構造方法如在html裡面構造一樣。使用<table><tr><tb>标簽來控制行和列以及内容。

同時還可以在table裡面設定屬性,如<table border='1'>、<td width='100'><br>等等。

其中跨行就用rowspan屬性來設定,如:"<td  rowspan=5>表示跨5行(合并單元格)。

原來還有這樣好的方法,于是按照這種方式寫了代碼,調試生成,效果非常好,上面的圖就是用table的方法生成的。

下面我貼上一些執行個體代碼:

ExpMaterialsToExcelModel.cs(這個是用來存将導出的資料類結構)

public class ExpMaterialsToExcelModel
    {
        /// <summary>
        /// 流程名稱
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 審批事項
        /// </summary>
        public List<Category> Categorys { get; set; }

        public ExpMaterialsToExcelModel()
        {
            Categorys = new List<Category>();
        }
    }

    public class Category
    {
        /// <summary>
        /// 審批事項名稱
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 承辦科室
        /// </summary>
        public string Department { get; set; }

        /// <summary>
        /// 聯系人及電話
        /// </summary>
        public string PersonAndPhone { get; set; }

        /// <summary>
        /// 材料
        /// </summary>
        public List<MaterialsInfo> MaterialsList { get; set; }

        public Category()
        {
            MaterialsList = new List<MaterialsInfo>();
        }
    }

    public class MaterialsInfo
    {
        /// <summary>
        /// 排号順序
        /// </summary>
        public int SortNumb { get; set; }

        /// <summary>
        /// 材料名稱
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 來源類型
        /// </summary>
        public string SourceType { get; set; }

        /// <summary>
        /// 來源
        /// </summary>
        public string Source { get; set; }

        /// <summary>
        /// 備注
        /// </summary>
        public string Note { get; set; }
    }           

ExpMaterialsToExcelModel.cs(從資料庫讀取資料)

private ExpMaterialsToExcelModel GetExpData(int processId)
        {
		代碼略。。。
	}           

GetExpString(生成導出的table文本)重點

private StringBuilder GetExpString(ExpMaterialsToExcelModel tableData)
        {
            int rowCount = tableData.Categorys.Sum(category => category.MaterialsList.Count);

            var str = new StringBuilder();
            str.Append("<meta http-equiv=\"content-type\" content=\"application/excel;charset=utf-8\" />");
            str.Append("<table border='1'>");
            str.Append("<tr>");
            str.AppendFormat("<td width='100' rowspan='{0}'>{1}</td>", rowCount, tableData.Name);

            for (int i = 0; i < tableData.Categorys.Count; i++)
            {
                if (i > 0)
                {
                    str.Append("<tr>");
                }

                var category = tableData.Categorys[i];

                rowCount = category.MaterialsList.Count;
                str.AppendFormat("<td width='100' rowspan='{0}'>{1}</td>", rowCount, category.Name);

                str.AppendFormat("<td width='100'>{0}</td>", category.MaterialsList[0].SourceType);
                str.AppendFormat("<td width='200'>{0}.{1}</td>", category.MaterialsList[0].SortNumb,
                                 category.MaterialsList[0].Name);
                str.AppendFormat("<td width='100'>{0}</td>", category.MaterialsList[0].Note);

                str.AppendFormat("<td width='100' rowspan='{0}'>{1}</td>", rowCount, category.Department);
                str.AppendFormat("<td width='100' rowspan='{0}'>{1}</td>", rowCount, category.PersonAndPhone);
                str.Append("</tr>");

                for (int j = 1; j < category.MaterialsList.Count; j++)
                {
                    str.Append("<tr>");
                    str.AppendFormat("<td width='100'>{0}</td>", category.MaterialsList[j].SourceType);
                    str.AppendFormat("<td width='200'>{0}.{1}</td>", category.MaterialsList[j].SortNumb,
                                     category.MaterialsList[j].Name);
                    str.AppendFormat("<td width='100'>{0}</td>", category.MaterialsList[j].Note);
                    str.Append("</tr>");
                }
            }

            str.Append("</table>");

            return str;
        }           

ExportData方法(頁面請求,傳回和下載下傳檔案)

/// <summary>
        /// 導出資料
        /// </summary>
        public ActionResult ExportData(int processId)
        {
            var tableData = GetExpData(processId);
            var tableString = GetExpString(tableData);

            // 輸出Excel
            string fileName = tableData.Name + "材料清單";
            Response.ClearContent();
            Response.AddHeader("content-disposition", 
                string.Format("attachment; filename={0}.xls", 
                HttpUtility.UrlEncode(fileName, Encoding.UTF8)));
            Response.ContentType = "application/excel";
            Response.Charset = "utf-8";
            Response.Write(tableString);
            Response.End();


            var myProcessId = processId;
            return RedirectToAction("Index", new { processId = myProcessId });
        }           

這種方法省去了去調用繁瑣的Excel API,非常友善。完全滿足合并單元格(合并行、合并列)的需求。