说明

最近接了一个任务,就是做一个列表的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单元格样式,这样才能具体的进行操作。

这个时候,我们就需要一个针对样式枚举,返回单元格样式的方法。

这样方法会进行以下几点操作

  1. 设置单元格通用的属性。我这里设置的是单元格边框。
  2. 针对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导出公共类的更多相关文章

  1. .NET开发工具之Excel导出公共类

    来源:Pino晨 链接:cnblogs.com/chenxygx/p/5954870.html 说明 最近接了一个任务,就是做一个列表的Excel导出功能.并且有很多页面都会使用这个功能. 导出的Ex ...

  2. 偷懒小工具 - SSO单点登录通用类(可跨域)

    写在前面的话 上次发布过一篇同样标题的文章.但是因为跨域方面做得不太理想.我进行了修改,并重新分享给大家. 如果这篇文章对您有所帮助,请您点击一下推荐.以便有动力分享出更多的"偷懒小工具&q ...

  3. 偷懒小工具 - SSO单点登录通用类(可跨域)(上)

    目的  目的很明确,就是搭建单点登录的帮助类,并且是一贯的极简风格(调用方法保持5行以内). 并且与其他类库,关联性降低.所以,不使用WebAPI或者WebService等. 思路   因为上次有朋友 ...

  4. Excel导出公共函数

    /// <summary> /// 将一组对象导出成EXCEL /// </summary> /// <typeparam name="T">要 ...

  5. Excel 导出通用类

    public class ExportToExcelHelper { public static void ExportExcel(DataTable dt) { try { //创建一个工作簿 IW ...

  6. POI导出复杂的excel;excel公共样式类;excel拼接定制类;数据科学计数法转为普通值

    一.excel公共样式类(包含数据科学计数法转为普通值) package com.thinkgem.jeesite.common.utils.excel; import org.apache.poi. ...

  7. 关于Excel导出实例(适合新手,比较详细)

    需要源代码的可以加我微信好友gqljxg1514 1,首先配置依赖pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0&q ...

  8. 第十篇 一个利用反射实现的Excel导出

    前些天写了个导出Excel的公共类,因为项目中也需要上传Excel,没有利用Office组件,(PS:Office在上传文件时候,Excel进程无法关闭,会导致上传和打开失败)有的说利用Kill把进程 ...

  9. 利用ncurses库开发终端工具箱(1)—— ToDoList小工具开发

    准备工作 腾讯云服务器(Ubuntu),C++编程语言 由于想输出界面中包含中文,所以安装库 libncursesw5,依次输入下面三行命令 sudo apt-get install libncurs ...

随机推荐

  1. ASP.NET Core Web API Cassandra CRUD 操作

    在本文中,我们将创建一个简单的 Web API 来实现对一个 “todo” 列表的 CRUD 操作,使用 Apache Cassandra 来存储数据,在这里不会创建 UI ,Web API 的测试将 ...

  2. 嵌入式:J-link刷固件(坑)

    1.上电,短接ERASE,>10秒后,拔USB. 2.短接TST,上电,>10秒后,拔USB. 3.安装驱动.(看别人教程,下载到INF文件,WIN7不能右击安装,好,换虚拟机XP) 4. ...

  3. ViewPager之引导页

    一.概述 ViewPager是android-support-v4中提供的类,它是一个容器类,常用于页面之间的切换. 本文介绍ViewPager最基础的应用:在多个View之间进行切换,亦即ViewP ...

  4. 人工智能AI-机器视觉CV-数据挖掘DM-机器学习ML-神经网络-[资料集合贴]

    说明:这个贴用于收集笔者能力范围内收集收藏并认为有用的资料,方便各方参考,免去到处找寻之苦,提升信息的交叉引用价值.仅供参考,不作为必然的推荐倾向.如涉及版权等问题请相关人员联系笔者,谢谢. |博客| ...

  5. Squirrel: 通用SQL、NoSQL客户端

    安装 配置数据库 配置驱动 配置连接 如果你的工作中,需要使用到多个数据库,又不想在多种客户端之间切换来切换去.那么就需要找一款支持多数据库的客户端工具了.如果你要连接多个关系型数据库,你就可以使用N ...

  6. 启动/关闭oracle服务有三种方式

    启动oracle服务有三种方式: 1 从控制面板 2 使用MS-DOS命令 3 通过Oracle Administration Assistant for WindowsNT -通过控制面板启动ora ...

  7. 用lumen构建API的相关流程

    概述 Lumen是一个基于Laravel的微框架,主要用于小型应用和微服务,专注于性能和速度的优化,该框架一个重要的应用就是构建 RESTAPI. 为什么用Lumen构建REST API Lumen访 ...

  8. 运维之网络安全抓包—— WireShark 和 tcpdump

    ------------------------------------------------本文章只解释抓包工具的捕获器和过滤器的说明,以及简单使用,应付日常而已----------------- ...

  9. inotify+rsync实现实时同步部署

    1.1.架构规划 1.1.1架构规划准备 服务器系统 角色 IP Centos6.7 x86_64 NFS服务器端(NFS-server-inotify-tools) 192.168.1.14 Cen ...

  10. Warm myself by my hand

    周末的尾巴了. 前几天白日里的气温降到10摄氏度以下,穿上了秋裤.隔天跑一次步,晚上九点多,5公里,25分钟左右.换上薄薄的运动裤,两件运动衣.一出宿舍门就没觉得冷,跑着跑着就愈加热了起来.遇到的问题 ...