偷懒小工具 - Excel导出公共类
说明
最近接了一个任务,就是做一个列表的Excel导出功能。并且有很多页面都会使用这个功能。
导出的Excel大体格式如图
很简单的列表,标题加背景色,然后不同类型,显示方式不一样。对齐方式不一样。不同页面除了内容以外,大体形式都差不多。
当时本来是想直接用NPOI,IRow ICell。这样进行拼接页面,最简单也最方便。
但是很多页面,都进行这种类似的设计。我实在是懒得做这种重复功能。所以花了一点时间,整理了一下帮助类。
使用
做好这个帮助类以后只要进行两点调用
1.制作导出Excel的数据模型。这个没办法,毕竟要告诉系统,你要导出的内容都是什么东西。省略不了。
2.调用帮助类的导出方法。初始化一下,ExportExcel(导出的数据集合); 然后Excel就直接下载完毕了。
ExcelDownload downLoad = new ExcelDownload("员工信息", "年度员工汇总");
downLoad.ExportExcel(testList);
思路和过程
接着,说一下我的思路和具体写法。做的不到,想的不周全的地方。还希望各位告知。源码地址在文章结尾处。
帮助类
因为我想做成使用最简单的类,所以我只暴露一个公共方法。其余一切都进行隐藏。
但是因为可能需要导出的其他格式的Excel,那么我也可以接受一个自定义的Excel填充方法。
这里我定义一个ExportExcel方法接受数据集合。并且添加一个重载方法,接受自定义Excel填充方法。
/// <summary>
/// 导出Excel
/// </summary>
/// <param name="list">导出模型数据</param>
public void ExportExcel<T>(IEnumerable<T> list)
{
GenerateExcel(list);
DownLoadExcel();
}
/// <summary>
/// 导出Excel
/// </summary>
/// <param name="excelSetMethod">操作Excel方法</param>
public void ExportExcel(Action<IWorkbook, ISheet> excelSetMethod)
{
excelSetMethod.Invoke(hssfWork, hssfSheet);
DownLoadExcel();
}
导出方法
方法里面的私有方法,稍后会详细说明。这里调用这一个方法,普通导出和自定义导出,均可实现。
模型和样式
首先要考虑的问题就是单元格的样式问题和列头名称问题。
如果不是公共方法,就非常简单了,直接在方法里面ICellStyle。但是既然是偷懒,肯定不能这样,而且又要考虑到可以自定义。
那么,我采用的方法就是给模型实体类使用Attribute。可以在实体类上面针对不同字段,设定不同的显示样式。
缺点也很显而易见,使用反射执行效率会稍微慢一点(各位有其他方法可以说一下)。
实体类的定义就如同下面
public class UserManagerTest
{
[ExcelInfo("名称")]
public string Name { get; set; }
[ExcelInfo("年龄", ExcelStyle = ExcelStyle.left)]
public int Old { get; set; }
[ExcelInfo("金额", ExcelStyle = ExcelStyle.money)]
public double Money { get; set; }
[ExcelInfo("时间", ExcelStyle = ExcelStyle.date | ExcelStyle.right)]
public DateTime CreateDate { get; set; }
}
实体类
可以定义中文名称显示标题、设置单元格样式、设置单元格宽度。
目前允许自定义的就这三个。
public class ExcelInfoAttribute : Attribute
{
/// <summary>
/// 显示中文名
/// </summary>
public string Name { get; set; } /// <summary>
/// 列宽
/// </summary>
public int Width { get; set; } /// <summary>
/// 列样式
/// </summary>
public ExcelStyle ExcelStyle { get; set; } /// <summary>
/// 默认左对齐,宽度2800
/// </summary>
public ExcelInfoAttribute(string name)
{
Name = name;
Width = ;
ExcelStyle = ExcelStyle.left;
}
}
单元格特性
ExcelStyle,是列样式的一个枚举,针对不同的值,会进行不同的单元格设置。
因为有可能一个单元格进行多种设置,例如时间格式并且右对齐。所以使用Flags,让其可以形成组。
[Flags]
public enum ExcelStyle
{
/// <summary>
/// 标题灰色背景
/// </summary>
title = 0x001, /// <summary>
/// 左对齐
/// </summary>
left = 0x002, /// <summary>
/// 右对齐
/// </summary>
right = 0x004, /// <summary>
/// 时间格式,右对齐
/// </summary>
date = 0x008, /// <summary>
/// 金钱格式,右对齐
/// </summary>
money = 0x016
}
样式枚举
样式有了接下来就要把他对应成ICellStyle单元格样式,这样才能具体的进行操作。
这个时候,我们就需要一个针对样式枚举,返回单元格样式的方法。
这样方法会进行以下几点操作
- 设置单元格通用的属性。我这里设置的是单元格边框。
- 针对Flags组,分别进行对应的单元格设定,并且最终返回一个ICellStyle。
单元格设定,因为样式会很多。所以这里使用了多态代替判断条件。
并且使用了一个键值对,来实现享元设计模式的思路。
internal static class ExcelStyleMessage
{
/// <summary>
/// 样式集合
/// </summary>
private static Dictionary<string, ICellStyle> styleList { get; set; } static ExcelStyleMessage()
{
styleList = new Dictionary<string, ICellStyle>();
} /// <summary>
/// 获取枚举对应CellStyle
/// </summary>
/// <param name="excelStyle">Excel样式枚举</param>
/// <returns></returns>
internal static ICellStyle GetCellStyle<T>(T workbook, ExcelStyle excelStyle) where T : IWorkbook
{
if (styleList.ContainsKey(excelStyle.ToString()))
{
return styleList[excelStyle.ToString()];
}
ICellStyle _cellStyle = workbook.CreateCellStyle();
_cellStyle.BorderTop = BorderStyle.Thin;
_cellStyle.BorderRight = BorderStyle.Thin;
_cellStyle.BorderLeft = BorderStyle.Thin;
_cellStyle.BorderBottom = BorderStyle.Thin;
CellStyleMethod styleMethod;
if (excelStyle.ToString().IndexOf(',') > -)
{
foreach (var styleItem in excelStyle.ToString().Replace(" ", "").Split(','))
{
if (Enum.IsDefined(typeof(ExcelStyle), styleItem))
{
ExcelStyle styleModel = (ExcelStyle)Enum.Parse(typeof(ExcelStyle), styleItem, true);
styleMethod = GetStyleMethod(styleModel);
styleMethod.SetCell(_cellStyle);
}
}
return _cellStyle;
}
styleMethod = GetStyleMethod(excelStyle);
styleMethod.SetCell(_cellStyle);
styleList.Add(excelStyle.ToString(), _cellStyle);
return _cellStyle;
} /// <summary>
/// 根据枚举加载对应操作类
/// </summary>
/// <param name="excelStyle">样式枚举</param>
/// <returns>操作类</returns>
private static CellStyleMethod GetStyleMethod(ExcelStyle excelStyle)
{
switch (excelStyle)
{
case ExcelStyle.title:
return new TitleBackgroundMethod(); case ExcelStyle.left:
return new LeftAligmentMethod(); case ExcelStyle.right:
return new RightAligmentMethod(); case ExcelStyle.date:
return new DateFormatMethod(); case ExcelStyle.money:
return new MoneyFormatMethod(); default:
throw new ArgumentException("参数无效");
}
}
}
Excel样式操作
具体样式的多态,我就不粘贴了。就是一个很简单的抽象类,然后集成实现SetCell。
抽象类定义了workbook字段,用于创建IDataFormat。具体可以看最底下的源码地址。
帮助类思路
首先考虑一下需要全局的字段和属性。Excel名称,页签名称,IWorkbook,ISheet。
那么,我们定义这四个,并且在构造函数里给他们赋值。
private readonly string _excelName;
private readonly string _excelSheetName;
private IWorkbook hssfWork { get; set; }
private ISheet hssfSheet { get; set; } /// <summary>
/// 初始化Excel相关信息
/// </summary>
/// <param name="ExcelName">Excel名称</param>
/// <param name="ExcelSheetName">初始页签名称</param>
public ExcelDownload(string ExcelName, string ExcelSheetName)
{
_excelName = ExcelName;
_excelSheetName = ExcelSheetName;
hssfWork = new HSSFWorkbook();
hssfSheet = hssfWork.CreateSheet(_excelSheetName);
}
构造函数
自定义的Excel填充,就是一个执行然后下载。我们忽略,说一下通用的方法。
public void ExportExcel<T>(IEnumerable<T> list)
{
GenerateExcel(list);
DownLoadExcel();
}
执行两个方法,1.生成Excel,2.下载Excel
生成Excel
在生成方法里面,我们给样式的workbook赋值,然后设置Excel的列表和单元格内容。
private void GenerateExcel<T>(IEnumerable<T> list)
{
if (list == null)
{
return;
}
CellStyleMethod.workbook = hssfWork;
Dictionary<PropertyInfo, ExcelInfoAttribute> _excelInfos = GetPropInfo<T>();
SetExcelTitle(_excelInfos);
SetExcelContent(list, _excelInfos);
}
GetPropInfo 是一个反射的方法,反射实体类中的特性,并返回属性和样式特性的键值对。这个在设置里面会有用途。
private Dictionary<PropertyInfo, ExcelInfoAttribute> GetPropInfo<T>()
{
Dictionary<PropertyInfo, ExcelInfoAttribute> _infos = new Dictionary<PropertyInfo, ExcelInfoAttribute>();
Type _type = typeof(T);
PropertyInfo[] _propInfos = _type.GetProperties();
foreach (var propInfo in _propInfos)
{
object[] objAttrs = propInfo.GetCustomAttributes(typeof(ExcelInfoAttribute), true);
if (objAttrs.Length > )
{
ExcelInfoAttribute attr = objAttrs[] as ExcelInfoAttribute;
if (attr != null)
{
_infos.Add(propInfo, attr);
}
}
}
return _infos;
}
GetPropInfo
SetExcelTitle 是设置Excel的标题。
private void SetExcelTitle(Dictionary<PropertyInfo, ExcelInfoAttribute> excelInfos)
{
IRow rowTitle = hssfSheet.CreateRow();
int _cellIndex = ;
foreach (var item in excelInfos)
{
ICell celltitle = rowTitle.CreateCell(_cellIndex);
celltitle.CellStyle = ExcelStyleMessage.GetCellStyle(hssfWork, ExcelStyle.title);
celltitle.SetCellValue(item.Value.Name); hssfSheet.SetColumnWidth(_cellIndex, item.Value.Width);
_cellIndex++;
}
}
SetTitle
SetExcelContent 是设置Excel的内容
private void SetExcelContent<T>(IEnumerable<T> list, Dictionary<PropertyInfo, ExcelInfoAttribute> _excelInfos)
{
int _rowNum = ;
Dictionary<string, ICellStyle> cellStyleList = new Dictionary<string, ICellStyle>();
foreach (T rowItem in list)
{
int _rowCell = ;
IRow _rowValue = hssfSheet.CreateRow(_rowNum);
foreach (var cellItem in _excelInfos)
{
object _cellItemValue = cellItem.Key.GetValue(rowItem);
ICell _cell = _rowValue.CreateCell(_rowCell);
if (!cellStyleList.ContainsKey(cellItem.Value.ExcelStyle.ToString()))
{
ICellStyle _cellStyle = ExcelStyleMessage.GetCellStyle(hssfWork, cellItem.Value.ExcelStyle);
cellStyleList.Add(cellItem.Value.ExcelStyle.ToString(), _cellStyle);
}
SetCellValue(cellItem, _cellItemValue, _cell);
_cell.CellStyle = cellStyleList[cellItem.Value.ExcelStyle.ToString()];
_rowCell++;
}
_rowNum++;
}
}
SetContent
这里就是使用键值对的属性,来获取Value值的。然后因为返回的object,所以我们进行一下类型转换。
SetCellValue 就是根据Type,来进行强制转换。
下载Excel
下载就更简单了,我们使用FileStream,进行一个临时存储。将NPOI写入这个文件中。然后将其转换成字节流,输出出来。
/// <summary>
/// 导出Excel操作
/// </summary>
private void DownLoadExcel()
{
string _path = TemporarySave();
FileStream fileStream = new FileStream(_path, FileMode.Open);
int fileContent = (int)fileStream.Length;
byte[] byData = new byte[fileContent];
fileStream.Read(byData, , fileContent);
fileStream.Close();
File.Delete(_path);
DownLoadExcel(byData);
} /// <summary>
/// 临时保存
/// </summary>
/// <returns></returns>
private string TemporarySave()
{
string _path = AppDomain.CurrentDomain.BaseDirectory;
_path += string.Format(@"\TemporarySave{0}.xlsx", DateTime.Now.ToString("hhmmss"));
using (FileStream file = new FileStream(_path, FileMode.Create))
{
hssfWork.Write(file);
file.Close();
}
return _path;
} /// <summary>
/// 下载
/// </summary>
private void DownLoadExcel(byte[] byData)
{
HttpContext.Current.Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}.xls", _excelName));
HttpContext.Current.Response.BinaryWrite(byData);
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.Close();
}
下载方法
至此,这个帮助类的雏形,就算完成了。说着挺简单的,但是连想带写,也还是用了一下午。
因为,平时很少写这种博客,都是知识点记录。所以可能措辞方面不太妥当,请各位见谅。
源码地址:https://github.com/chenxygx/ExcelExportHelper
写的不好,多多见谅。
偷懒小工具 - Excel导出公共类的更多相关文章
- .NET开发工具之Excel导出公共类
来源:Pino晨 链接:cnblogs.com/chenxygx/p/5954870.html 说明 最近接了一个任务,就是做一个列表的Excel导出功能.并且有很多页面都会使用这个功能. 导出的Ex ...
- 偷懒小工具 - SSO单点登录通用类(可跨域)
写在前面的话 上次发布过一篇同样标题的文章.但是因为跨域方面做得不太理想.我进行了修改,并重新分享给大家. 如果这篇文章对您有所帮助,请您点击一下推荐.以便有动力分享出更多的"偷懒小工具&q ...
- 偷懒小工具 - SSO单点登录通用类(可跨域)(上)
目的 目的很明确,就是搭建单点登录的帮助类,并且是一贯的极简风格(调用方法保持5行以内). 并且与其他类库,关联性降低.所以,不使用WebAPI或者WebService等. 思路 因为上次有朋友 ...
- Excel导出公共函数
/// <summary> /// 将一组对象导出成EXCEL /// </summary> /// <typeparam name="T">要 ...
- Excel 导出通用类
public class ExportToExcelHelper { public static void ExportExcel(DataTable dt) { try { //创建一个工作簿 IW ...
- POI导出复杂的excel;excel公共样式类;excel拼接定制类;数据科学计数法转为普通值
一.excel公共样式类(包含数据科学计数法转为普通值) package com.thinkgem.jeesite.common.utils.excel; import org.apache.poi. ...
- 关于Excel导出实例(适合新手,比较详细)
需要源代码的可以加我微信好友gqljxg1514 1,首先配置依赖pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0&q ...
- 第十篇 一个利用反射实现的Excel导出
前些天写了个导出Excel的公共类,因为项目中也需要上传Excel,没有利用Office组件,(PS:Office在上传文件时候,Excel进程无法关闭,会导致上传和打开失败)有的说利用Kill把进程 ...
- 利用ncurses库开发终端工具箱(1)—— ToDoList小工具开发
准备工作 腾讯云服务器(Ubuntu),C++编程语言 由于想输出界面中包含中文,所以安装库 libncursesw5,依次输入下面三行命令 sudo apt-get install libncurs ...
随机推荐
- 2016年6月份那些最实用的 jQuery 插件专辑
jQuery 是一个快速.流行的 JavaScript 库,jQuery 用于文档处理.事件处理.动画和 Ajax 交互非常简单,学习曲线也很平坦.2016年6月的 jQuery 插件专辑里,我们选择 ...
- 如何在Node.js中合并两个复杂对象
通常情况下,在Node.js中我们可以通过underscore的extend或者lodash的merge来合并两个对象,但是对于像下面这种复杂的对象,要如何来应对呢? 例如我有以下两个object: ...
- Mockjs,模拟数据生成器
(推荐使用)Mock.js是一款模拟数据生成器,旨在帮助前端攻城师独立于后端进行开发,帮助编写单元测试. 提供了以下模拟功能: 1. 根据数据模板生成模拟数据. 2. 模拟Ajax请求,生成并返回模拟 ...
- 浏览器渲染引擎,提高css渲染速度。
一.渲染引擎渲染引擎的职责是……渲染,也就是把请求的内容显示到浏览器屏幕上.默认情况下渲染引擎可以显示HTML,XML文档以及图片. 通过插件(浏览器扩展)它可以显示其它类型文档. 二.各种渲染引擎我 ...
- Android中使用Notification实现宽视图通知栏(Notification示例二)
Notification是在你的应用常规界面之外展示的消息.当app让系统发送一个消息的时候,消息首先以图表的形式显示在通知栏.要查看消息的详情需要进入通知抽屉(notificationdrawer) ...
- zend studio 快捷键
复制当前行:ctrl+alt+↓ 删除当前行:Ctrl+d 行注释:Ctrl+/ 快注释(先选中要注释的代码):Ctrl+shift+/ 提示助手(方便函数等补全):alt+/ 代码格式化:Ctrl+ ...
- C#设计模式:原型模式(Prototype)及深拷贝、浅拷贝
原型模式(Prototype) 定义: 原型模式:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象.被复制的实例被称为原型,这个原型是可定制的. Prototype Pattern也是一 ...
- SQL Server 进阶 01 数据库的设计
SQL Server 进阶 01 数据库的设计 本篇目录 课程内容回顾及介绍 为什么需要规范的数据库设计 设计数据库的步骤 绘制E-R(实体-关系)图 实体-关系模型 如何将E-R图转换为表 数据规范 ...
- Context值和bool开关
Context值和bool开关的相关内容 Context值分为2种 系统默认的context值 服务的context值 Context值的作用 主要是防止有未知文件进入目录文件之中(如将病毒拷贝到 ...
- 基本shell编程【3】- 常用的工具awk\sed\sort\uniq\od
awk awk是个很好用的东西,大量使用在linux系统分析的结果展示处理上.并且可以使用管道, input | awk '' | output 1.首先要知道形式 awk 'command' fi ...