轮子 · DotNET · 一个后台生成Excel导出的方法

244 阅读5分钟

使用

调用方法

通过ExcelsList.fm生成一个处理数据的方法,此处用的lambdaExcelsList的构造方法里,必须传的是列名,其他的如果没有就空着,下载的文件名无所谓,前台用的时候也可以给名字

    DataTable ListData = 各种获取数据;

     
    //列的信息
    List<ExcelsList> dc=new List<ExcelsList>();
    ExcelsList.fm isend = (string cellname,DataRow rows) =>
    {
        if (rows[cellname].ToString() == "1") return "待化解";
        if (rows[cellname].ToString() == "2") return "已化解";
        return "";
    };
    ExcelsList.fm getstate= (string cellname,DataRow rows)=> { 
        if (rows["isend"].ToString() == "1") { // 未办结
            if (DateTime.Now.AddDays(Convert.ToInt32(rows["LimitDays"])) < Convert.ToDateTime(rows[cellname]))
            {
                //return "超期未办结";
                return "<td  width='120' height='20'  style=\"background-color: #EE2C2C;\">超期未办结</td>";
            }
            else if (DateTime.Now.AddDays(Convert.ToInt32(rows["LimitDays"]) * 2 / 3) < Convert.ToDateTime(rows[cellname]))
            {
                //return "黄色预警";
                return "<td  width='120' height='20'  style=\"background-color: #FFD700;\">黄色预警</td>";
            }
            else
            {   //化解中
                return "<td  width='120' height='20'  style=\"background-color: #32CD32;\">化解中</td>";
            }
        } else {     
            return "已办结";
        }
    };
    //添加所有显示的列
    dc.Add(new ExcelsList("pctime", "状态", getstate));
    dc.Add(new ExcelsList("id", "排查编号"));
    dc.Add(new ExcelsList("pctime", "排查时间"));
    dc.Add(new ExcelsList("name", "信访人姓名"));
    dc.Add(new ExcelsList("addorg", "排查单位"));
    dc.Add(new ExcelsList("nowexeorg", "承办单位"));
    dc.Add(new ExcelsList("handlemode", "办理方式"));
    dc.Add(new ExcelsList("isend", "是否化解", isend)); 

    //导出   
    HPK.Utilities.DeriveExcel.DataTableandExcelsListToExcel(Response, ListData, dc, "化解" + DateTime.Now.ToString("yyyy-MM-dd HHmmss"));

前台

这边千变万化的,具体情况具体对待,这里先记下一种

调用写在MVC的Controller里的方法

var a = document.createElement("a");
a.href = "url"; 
a.download = '化解' + formatDate(new Date(), "yyyy-MM-dd-hhmmss.xls"); //如果不填就是后台默认的名字
a.click();

实现导出的部分

封装类

用来保存真实列名、显示列名和处理内容的方法

/// <summary>
/// 列的信息
/// </summary>
public class ExcelsList
{

    #region  封装字段
    //显示名称
    public string zh_name;

    //所在列名
    public string col_name;

    //过滤方法
    public fm thisfm;
    #endregion

    #region 方法
    /// <summary>
    /// 导出excel列的信息
    /// </summary>
    /// <param name="_col">列名</param>
    /// <param name="_zh">列备注</param>
    /// <param name="_thisfm">处理值的方法</param>
    public ExcelsList(string _col, string _zh = null, fm _thisfm = null)
    {
        this.col_name = _col;

        this.zh_name = _zh;

        this.thisfm = _thisfm;

    }
    /// <summary>
    /// 过滤的方法
    /// </summary>
    /// <param name="str">输入值</param>
    /// <returns>返回一个方法</returns>
    public delegate string fm(string str,DataRow dr = null);

    /// <summary>
    /// 获取筛选后的返回值的方法
    /// </summary> 
    /// <returns>返回值</returns>
    public string retstr(DataRow ROW)
    {
        string str;

        ///是否过滤规则
        if (this.thisfm != null)
        {
            str = this.thisfm(this.col_name, ROW);
        }
        else
        {
            str = ROW[this.col_name].ToString();
        }

        ///是否规定格式
        if (!str.StartsWith("<td"))
        {
            str = "<td  class='strs'>" + str + "</td>";
        }

        ///返回值
        return str;
    }
    #endregion

}

后台

HttpResponseBase Response视情况可以不用传值进来,直接HttpContext.Current.Response

/// <summary>
/// DataTable导出Excel
/// </summary>
/// <param name="data">数据源</param>
/// <param name="DataColumn">导出的字段设置</param>
/// <param name="fileName">文件名称&表头</param>
public static void DataTableAndExcellistToExcel(HttpResponseBase Response, DataTable data, List<ExcelsList> DataColumn, string fileName="Excel")
{
    Response.ContentType = "application/vnd.ms-excel";
    Response.ContentEncoding = System.Text.Encoding.UTF8;
    Response.Charset = "Utf-8";
    Response.AppendHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(fileName + ".xls", System.Text.Encoding.UTF8));//给一个默认的名字,如果前台没给名字,那下载时候就是显示这个
    StringBuilder sbHtml = new StringBuilder();
    sbHtml.AppendLine("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");
    sbHtml.AppendLine("<style> .strs{ mso-number-format:\"\\@\";&emsp;}  </style>");
    sbHtml.AppendLine("<table cellspacing=\"0\" cellpadding=\"5\" rules=\"all\" border=\"1\">");
    //表头
    sbHtml.AppendLine("<tr height=\"49.33\" style='height:37.00pt;mso-height-source:userset;mso-height-alt:740;'>");
    sbHtml.AppendLine("<td height=\"49.33\" width=\"1257\" colspan=\"" + DataColumn .Count.ToString()+ "\" style='height:37.00pt;width:942.75pt;border-right:none;border-bottom:none;' x:str>" + fileName + "</td>");
    sbHtml.AppendLine("</tr>");
    //写出列名
    sbHtml.AppendLine("<tr style=\"background-color: #FFE88C;font-weight: bold; white-space: nowrap;\">");
    foreach (ExcelsList item in DataColumn)
    {
        sbHtml.AppendLine("<td>" + item.zh_name ?? item.col_name + "</td>");
    }
    sbHtml.AppendLine("</tr>");
    //写数据
    foreach (DataRow row in data.Rows)
    {
        sbHtml.Append("<tr>");
        foreach (ExcelsList item in DataColumn)
        {
            sbHtml.Append(item.retstr(row));
        }
        sbHtml.AppendLine("</tr>");
    }
    sbHtml.AppendLine("</table>");
    Response.Write(sbHtml.ToString());
    Response.End();
}

踩过的坑

1.导出的长串数字自动转换 科学计数法 显示

例如 201805241723197 ,会在excel 里显示为2.01805E+16,这个解决方法是

· x:str属性
有时候不太好用

str = "<td x:str=\"" + str + "\">" + str + "</td>";

· MSO-NUMBER-FORMAT
这个可以用

str = "<td STYLE='MSO-NUMBER-FORMAT:\\@'>" + str + "</td>";

或者放进css里

sbHtml.AppendLine("<style> .strs{ mso-number-format:\"\\@\";&emsp;}  </style>");

...

str = "<td  class='strs'>" + str + "</td>";

其他的MSO-NUMBER-FORMAT

mso-number-format:"0"&emsp;NO&emsp;Decimals&emsp;  
mso-number-format:"0\.000"&emsp;3&emsp;Decimals&emsp;  
mso-number-format:"\#\,\#\#0\.000"&emsp;Comma&emsp;with&emsp;3&emsp;dec&emsp;  
mso-number-format:"mm\/dd\/yy"&emsp;Date7&emsp;  
mso-number-format:"mmmm\&emsp;d\,\&emsp;yyyy"&emsp;Date9&emsp;  
mso-number-format:"m\/d\/yy\&emsp;h\:mm\&emsp;AM\/PM"&emsp;D&emsp;-T&emsp;AMPM&emsp;  
mso-number-format:"Short&emsp;Date"&emsp;01/03/1998&emsp;  
mso-number-format:"Medium&emsp;Date"&emsp;01-mar-98&emsp;  
mso-number-format:"d\-mmm\-yyyy"&emsp;01-mar-1998&emsp;  
mso-number-format:"Short&emsp;Time"&emsp;5:16&emsp;  
mso-number-format:"Medium&emsp;Time"&emsp;5:16&emsp;am&emsp;  
mso-number-format:"Long&emsp;Time"&emsp;5:16:21:00&emsp;  
mso-number-format:"Percent"&emsp;Percent&emsp;-&emsp;two&emsp;decimals&emsp;  
mso-number-format:"0%"&emsp;Percent&emsp;-&emsp;no&emsp;decimals&emsp;  
mso-number-format:"0\.E+00"&emsp;Scientific&emsp;Notation&emsp;  
mso-number-format:"\@"&emsp;Text&emsp;  
mso-number-format:"\#\&emsp;???\/???"&emsp;Fractions&emsp;-&emsp;up&emsp;to&emsp;3&emsp;digits&emsp;(312/943)&emsp; 

2.Response.End()不知道为什么总是报错

从网上搜来的内容

Response.End()是终止服务器输出,但有可能会throw出线程异常,不处理即可,ms官方对该问题进行了解释,如果你不希望throw出线程错误,则可以采用相关解决方法。

HttpContext.Current.ApplicationInstance.CompleteRequest();代替后解决了