最近的项目中要大量用到 DataGrid 中的数据导出到 Excel 的方法,且数据量比较大。针对这个我做了一个导出 Excel 的帮助类。 之前在网上我搜索到很多导出 Excel 的代码,但是都不尽如人意。一般有下列几种方法:
1. 用 Excel.Application 对象逐单元格的填充数据。 这个办法由于是操作 COM 组件来进行,而 .NET 调用 COM 是要经过一层 wrapper 的,必然导致效率不高。还有一个原因是, Excel 不是针对多线程而设计的, 在大的并发需求下,可能会出现问题。因此一般这个方法只推荐适用于 Intranet.
2. 用 DataGrid 的 RenderControl 方法直接输出到一个 HtmlTextWriter 对象。 这个办法比较简单,因为是输出纯 html, 不涉及服务端 Excel 对象的初始化带来的开销。所以性能应该是比较好的。但是我看了现有的一些例子都是只能输出一页的, 也就是说不适用于分页的 DataGrid.
3. 用 OWC, 同样,由于是 COM 组件,存在性能问题。
针对这个情况我决定采用 2 作为最终输出的方法,在内存中构造不分页的 DataGrid, 然后 RenderControl 的方法。(要传入数据源作为参数)。 因为我加了自定义列标题和字段列表的功能,所以目前的数据源只能是类似 DataTable 这样的,至于一些其他可以绑定到 DataGrid 的类型比如 IListSource, IEnumerable, 由于内部不带有字段名称,标题信息等内容,分析起来非常复杂。简单起见,我忽略了这些,而仅仅接收 DataTable 作为数据源。
代码在这里:
//===============================================================================
//
// 从 DataGrid 或数据源中导出数据到 Excel 并提示下载的帮助类。
//
// Author: Roger Chen (木野狐)
// Date: 2005-1-27
// Version: 1.22
// History:
// v1.00 使用静态方法的形式实现该类,提供多种重载方式。
// v1.01 添加了对 DevExpress.Web.ASPxGrid.ASPxGrid 的直接导出支持。
// v1.20 改写为实体类。 减少了重复代码。
// v1.21 2005-2-1
// 修改了一个构造函数的重载形式中异常检测的代码。延迟到 Export() 方法。
// v1.22 2005-2-3
// 1. 修正了 Export() 方法中缺少 _titles != null 判断的 bug.
// 2. 修正了长的数字被 Excel 自动转换为科学计数法的毛病。
// (修改的办法来自 http://dotnet.aspx.cc)
//
//===============================================================================
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedBlockStart.gif) namespace RChen.Demos {
using System;
using System.IO;
using System.Data;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using System.Globalization;
using System.Collections;
using DevExpress.Web.ASPxGrid;
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public class ExcelHelper {
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ContractedSubBlock.gif) FieldsFields#region Fields
string _fileName;
DataTable _dataSource;
string[] _titles = null;
string[] _fields = null;
int _maxRecords = 1000;
#endregion
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ContractedSubBlock.gif) PropertiesProperties#region Properties
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /**//**//**//// <summary>
/// 限制输出到 Excel 的最大记录数。超出则抛出异常
/// </summary>
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public int MaxRecords {
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) set { _maxRecords = value; }
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) get { return _maxRecords; }
}
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /**//**//**//// <summary>
/// 输出到浏览器的 Excel 文件名
/// </summary>
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public string FileName {
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) set { _fileName = value; }
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) get { return _fileName; }
}
#endregion
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ContractedSubBlock.gif) .ctor.ctor#region .ctor
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /**//**//**//// <summary>
/// 构造函数
/// </summary>
/// <param name="titles">要输出到 Excel 的列标题的数组</param>
/// <param name="fields">要输出到 Excel 的字段名称数组</param>
/// <param name="dataSource">数据源</param>
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public ExcelHelper(string[] titles, string[] fields, DataTable dataSource): this(titles, dataSource) {
if (fields == null || fields.Length == 0)
throw new ArgumentNullException("fields");
if (titles.Length != fields.Length)
throw new ArgumentException("titles.Length != fields.Length", "fields");
_fields = fields;
}
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /**//**//**//// <summary>
/// 构造函数
/// </summary>
/// <param name="titles">要输出到 Excel 的列标题的数组</param>
/// <param name="dataSource">数据源</param>
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public ExcelHelper(string[] titles, DataTable dataSource): this(dataSource) {
if (titles == null || titles.Length == 0)
throw new ArgumentNullException("titles");
//if (titles.Length != dataSource.Columns.Count)
// throw new ArgumentException("titles.Length != dataSource.Columns.Count", "dataSource");
_titles = titles;
}
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /**//**//**//// <summary>
/// 构造函数
/// </summary>
/// <param name="dataSource">数据源</param>
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public ExcelHelper(DataTable dataSource) {
if (dataSource == null)
throw new ArgumentNullException("dataSource");
// maybe more checks needed here (IEnumerable, IList, IListSource, ) ???
// 很难判断,先简单的使用 DataTable
_dataSource = dataSource;
}
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public ExcelHelper() {}
#endregion
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ContractedSubBlock.gif) public Methodspublic Methods#region public Methods
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /**//**//**//// <summary>
/// 导出到 Excel 并提示下载
/// </summary>
/// <param name="dg">DataGrid</param>
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public void Export(DataGrid dg) {
if (dg == null)
throw new ArgumentNullException("dg");
if (dg.AllowPaging || dg.PageCount > 1)
throw new ArgumentException("paged DataGrid can't be exported.", "dg");
// 添加标题样式
dg.HeaderStyle.Font.Bold = true;
dg.HeaderStyle.BackColor = System.Drawing.Color.LightGray;
RenderExcel(dg);
}
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /**//**//**//// <summary>
/// 导出到 Excel 并提示下载
/// </summary>
/// <param name="xgrid">ASPxGrid</param>
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public void Export(DevExpress.Web.ASPxGrid.ASPxGrid xgrid) {
if (xgrid == null)
throw new ArgumentNullException("xgrid");
if (xgrid.PageCount > 1)
throw new ArgumentException("paged xgird not can't be exported.", "xgrid");
// 添加标题样式
xgrid.HeaderStyle.Font.Bold = true;
xgrid.HeaderStyle.BackColor = System.Drawing.Color.LightGray;
RenderExcel(xgrid);
}
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /**//**//**//// <summary>
/// 导出到 Excel 并提示下载
/// </summary>
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) public void Export() {
if (_dataSource == null)
throw new Exception("数据源尚未初始化");
if (_fields == null && _titles!=null && _titles.Length != _dataSource.Columns.Count)
throw new Exception("_titles.Length != _dataSource.Columns.Count");
if (_dataSource.Rows.Count > _maxRecords)
throw new Exception("导出数据条数超过限制。请设置 MaxRecords 属性以定义导出的最多记录数。");
DataGrid dg = new DataGrid();
dg.DataSource = _dataSource;
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) if (_titles == null) {
dg.AutoGenerateColumns = true;
}
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) else {
dg.AutoGenerateColumns = false;
int cnt = _titles.Length;
System.Web.UI.WebControls.BoundColumn col;
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) if (_fields == null) {
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) for (int i=0; i<cnt; i++) {
col = new System.Web.UI.WebControls.BoundColumn();
col.HeaderText = _titles[i];
col.DataField = _dataSource.Columns[i].ColumnName;
dg.Columns.Add(col);
}
}
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) else {
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) for (int i=0; i<cnt; i++) {
col = new System.Web.UI.WebControls.BoundColumn();
col.HeaderText = _titles[i];
col.DataField = _fields[i];
dg.Columns.Add(col);
}
}
}
// 添加标题样式
dg.HeaderStyle.Font.Bold = true;
dg.HeaderStyle.BackColor = System.Drawing.Color.LightGray;
dg.ItemDataBound += new DataGridItemEventHandler(DataGridItemDataBound);
dg.DataBind();
RenderExcel(dg);
}
#endregion
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ContractedSubBlock.gif) private Methodsprivate Methods#region private Methods
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) private void RenderExcel(Control c) {
// 确保有一个合法的输出文件名
if (_fileName == null || _fileName == string.Empty || !(_fileName.ToLower().EndsWith(".xls")))
_fileName = GetRandomFileName();
HttpResponse response = HttpContext.Current.Response;
response.Charset = "GB2312";
response.ContentEncoding = Encoding.GetEncoding("GB2312");
response.ContentType = "application/ms-excel/msword";
response.AppendHeader("Content-Disposition", "attachment;filename=" +
HttpUtility.UrlEncode(_fileName));
CultureInfo cult = new CultureInfo("zh-CN", true);
StringWriter sw = new StringWriter(cult);
HtmlTextWriter writer = new HtmlTextWriter(sw);
writer.WriteLine("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=GB2312\">");
DataGrid dg = c as DataGrid;
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) if (dg != null) {
dg.RenderControl(writer);
}
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) else {
ASPxGrid xgrid = c as ASPxGrid;
if (xgrid != null)
xgrid.RenderControl(writer);
else
throw new ArgumentException("only supports DataGrid or ASPxGrid.", "c");
}
c.Dispose();
response.Write(sw.ToString());
response.End();
}
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) /**//**//**//// <summary>
/// 得到一个随意的文件名
/// </summary>
/// <returns></returns>
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) private string GetRandomFileName() {
Random rnd = new Random((int) (DateTime.Now.Ticks));
string s = rnd.Next(Int32.MaxValue).ToString();
return DateTime.Now.ToShortDateString() + "_" + s + ".xls";
}
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) private void DataGridItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e) {
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedSubBlockStart.gif) if(e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) {
e.Item.Attributes.Add("style", "vnd.ms-excel.numberformat:@");
//e.Item.Cells[3].Attributes.Add("style", "vnd.ms-excel.numberformat:¥#,###.00");
}
}
#endregion
}
}
代码就是页面里这些了。不过忘记了写调用的例子,现在补上: 以下例子实际调用时需要在外面用 try 块捕获并处理异常。可能抛出的异常有 ParameterException, ParameterNullException, Exception 等。
// 1. 导出一个不分页的 DataGrid 到 Excel.
ExcelHelper helper = new ExcelHelper();
// 设置文件名(可省。省略则自动生成一个随机的文件名)
helper.FileName = "xxx.xls";
helper.Export(dg);
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/None.gif)
// 2. 导出分页的 DataGrid,需要同时指定他的数据源(DataTable)
DataTable dt = ;
ExcelHelper helper = new ExcelHelper(dt);
// 最大导出条数(可省)
helper.MaxRecords = 2000;
helper.Export();
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/None.gif)
// 3. 如果要指定列标题,这样调用:
DataTable dt = ;
ExcelHelper helper = new ExcelHelper(
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedBlockStart.gif) new string[] {"列标题1", "列标题2", },
dt);
helper.Export();
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/None.gif)
// 4. 如果还要指定字段名称, 这样调用(因为输出的字段名称不一定要和 DataTable 里字段名称的次序相同。
DataTable dt = ;
ExcelHelper helper = new ExcelHelper(
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedBlockStart.gif) new string[] {"列标题1", "列标题2", },
![](http://www.soft123.com/news/admin/Images/OutliningIndicators/ExpandedBlockStart.gif) new string[] {"字段名1", "字段名2", },
dt);
helper.Export(); |