这些天我做了一个下载Excel,上传Excel的功能,代码是抄的,控件是现成的,我就是把他们放一块了.先说下载Excel,就是从数据库中读出一些数据,然后将这些数据放到Excel中,最后就可以下载到本地了.
前台代码就是一个链接,然后这个链接的JS方法就是调用一个一般处理程序,在一般处理程序中写我那段拼凑Excel的代码.
首先将需要导出的数据拿出来,也就是Excel中的那几个字段,就比如我的需要员工编号,姓名,身份证号,组织和奖金金额.前面四项需要我从数据库中读出,而奖金金额则是应该下载完Excel后手动填写的.如果没有任何一张实体是和这个对应的,你可以选择自己建一个实体,并建一个该实体的集合类.集合类里可以不写任何方法.
[Serializable]
public classEmployeeDividedBonusImportTrans
{
/// <summary>
/// 员工Code
/// </summary>
public string EmployeeCode { get; set;}
/// <summary>
/// 员工编号
/// </summary>
[Description("员工编号")]
public string EmployeeIDNumber { get;set; }
/// <summary>
/// 身份证号
/// </summary>
[Description("证件号")]
public string IdentityCardCode { get;set; }
/// <summary>
/// 员工姓名
/// </summary>
[Description("员工姓名")]
public string EmployeeName { get; set;}
/// <summary>
/// 业务组织
/// </summary>
[Description("业务组织")]
public string OrgName { get; set; }
/// <summary>
/// 年终奖金额
/// </summary>
[Description("年终奖金")]
public decimal Amount { get; set; }
}</span>
而集合类就是这样
[Serializable]
public classEmployeeDividedBonusImportTransCollection :
EditableDataObjectCollectionBase<EmployeeDividedBonusImportTrans>
{
}
这样就能体现EmployeeDividedBonusImportTrans和EmployeeDividedBonusImportTransCollection是有一对多关系的.
实体类和集合类都准备好了之后,就该考虑一般处理程序的功能了,他的主要功能1,生成导出信息;2.将生成的导出信息生成到excel中;3.下载excel.
importCol =importCol.GenerateImportDetail(form.UnitCode);
if (importCol != null)
{
byte[] excel =importCol.ToExcel();
PageExtension.DownloadBytes(excel,string.Format("[{0}]的年终奖金额导出.xlsx", form.UnitName));
}
代码掐头去尾,根据命名大概能知道importCol就是集合类,然后在集合类中有GenerateImportDetail(),生成导出信息;ToExcel(),生成Excel和在PageExtension中有DownloadBytes下载Excel 3个方法.
对于GenerateImportDetail(),所做的功能就是,将实体类的字段值填满(除奖金金额),然后实体类变成集合.返回集合就可以了.
而ToExcel就是
/// <summary>
/// 生成Excel
/// </summary>
/// <returns></returns>
public byte[] ToExcel()
{
string[] dataName = {"EmployeeIDNumber", "OrgName","IdentityCardCode", "EmployeeName", "Amount" };
//集合转换为datatable
DataTable data =DataTableExtension.ToDataTable(this, dataName);
byte[] template =DocumentHelper.CreateDocumentAndTable("Sheet1", "A1","EmployeeDividedBonusImport", data.AsDataView(),ExcelTableStyles.Custom);
return template;
}
显示将5个列变为Datatable,然后将那个datatable变为填充好数据变成数组返回.
///<summary>
/// 转换为DataView 列名使用属性名,列Caption使用DescriptionAttribute
/// </summary>
/// <typeparamname="T">元素类型</typeparam>
/// <paramname="items">集合</param>
/// <paramname="propertyTypeNames"> </param>
/// <returns></returns>
public static DataTableToDataTable<T>(IEnumerable<T> items, params string[]propertyTypeNames)
{
//首先验证不能为空
items.NullCheck("items");
//然后将传入的数据的类型取出,因为this是集合类型,而集合类型继承的EditableDataObjectCollectionBase继承了IEnumerable<T>. T是实体类型
Type type = typeof(T);
//取得表结构
DataTable table =GetTableStruct(type, propertyTypeNames);
//然后将items也就是集合中的数据一条一条写到datatable中
//使用lamda表达式,obj就是集合中的实体类,一条一条遍历集合中的数据.
items.ForEach(obj =>
{
//先创建一行,然后给这一行赋值,最后一行一行都填充好了就变成table了
DataRow row = table.NewRow();
//获取到实体类型的属性,遍历属性信息
foreach (PropertyInfo prop intype.GetProperties())
{
//如果实体类的属性名和表的列名是一样的,就把该属性上的值,给拿出来赋给该列
if(table.Columns.Contains(prop.Name))
{
//将属性上的值拿出
object columnValue =prop.GetValue(obj, null);
//如果列是时间,并且是最小的时间,那么就让他为null
if (columnValue isDateTime && ((DateTime)columnValue) == DateTime.MinValue)
{
columnValue =DBNull.Value;
}
//最后给对应的列赋值
row[prop.Name] =columnValue;
}
}
//最后行组合成表
table.Rows.Add(row);
});
//提交datatable的更改
table.AcceptChanges();
//最后返回填充好了数据的datable
return table;
}
生成了table之后,还要将table中的数据填充到exceltable中
/// <summary>
///创建文档,并将数填充到ExcelTable中
///</summary>
///<param name="worksheetName">工作表名称</param>
///<param name="beginAddress">开始单元格</param>
///<param name="tableName">ExcelTable名称</param>
///<param name="dvData">数据源</param>
///<param name="tableStyle">ExcelTable 样式</param>
///<param name="isPrintHeaders"></param>
///<returns></returns>
publicstatic byte[] CreateDocumentAndTable(string worksheetName, string beginAddress,string tableName, System.Data.DataView dvData, ExcelTableStyles tableStyle)
{
//创建一个工作簿
WorkBookworkbook = WorkBook.CreateNew();
//创建工作单
WorkSheetworksheet = workbook.Sheets["sheet1"];
//给工作单命名
if(worksheetName.IsNotEmpty())
worksheet.Name= worksheetName;
//给databview的table命名
dvData.Table.TableName= tableName;
//将dvData中的数据填充到工作单中,往里看了下,没完没了了.
worksheet.LoadFromDataView(CellAddress.Parse(beginAddress),tableStyle, dvData, null);
//最后将工作簿转为二进制输出
returnworkbook.SaveAsBytes();
}
而将转为二进制输出是这样做的,
///<summary>
///将文件转换成二进制输出
///</summary>
///<returns></returns>
publicbyte[] SaveAsBytes()
{
//首先得有二进制的对象
Byte[]byRet = null;
using(MemoryStream excelStream = new MemoryStream())
{
Save(excelStream);
byRet= new byte[excelStream.Length];
longpos = excelStream.Position;
excelStream.Read(byRet,0, (int)excelStream.Length);
excelStream.Seek(pos,SeekOrigin.Begin);
returnbyRet;
}
}
核心是这一句,excelStream.Read(byRet,0, (int)excelStream.Length);从0开始,将文件整个读到byRet中,最后返回.
差不多从ToExcel中出来了,数据都填充好了,那么现在就是下载excel了.
PageExtension.DownloadBytes(excel,string.Format("[{0}]的年终奖金额导出.xlsx", form.UnitName));
/// <summary>
/// 下载二进制文件, 不分段下载, 适合小文件
/// </summary>
/// <paramname="bytes"></param>
/// <paramname="fileName"></param>
public static void DownloadBytes(byte[]bytes, string fileName)
{
//将文件名转为unicode编码的文件名
string encodedfileName =HttpUtility.UrlEncode(fileName, Encoding.UTF8);
//为当前的http请求设置http上下文对象
HttpContext context =HttpContext.Current;
//清空http上下文的response
context.Response.Clear();
//设置输出流的http MIME类型
context.Response.ContentType =MediaTypeNames.Text.Xml;
//将http头添加到输出流
context.Response.AppendHeader("content-disposition","attachment;fileName=" + encodedfileName);
//将二进制写入http输出流
context.Response.BinaryWrite(bytes);
//context.Response.End();
//结束
context.ApplicationInstance.CompleteRequest();
}
核心就是
context.Response.AppendHeader("content-disposition","attachment;fileName=" + encodedfileName);
context.Response.BinaryWrite(bytes);
将文件名设置好,然后将二进制的bytes写入http输出流.
然后文件就可以下载了.总结一小下,不深入不知道,一深入吓一跳.每段代码看起来都不多哦,看起来很简单哦,但是每个方法调了另一个方法,另一个方法调了另一个方法,然后就这样没玩没了了.封装的真是吓人,也确实是相当方便,我本身只用写一点代码,就能完成下载excel的功能.但是我所用的那一点代码,背后却是封装了无数的方法.