来源:Pino晨

链接:cnblogs.com/chenxygx/p/5954870.html

说明

最近接了一个任务,就是做一个列表的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 = 2800;

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。

单元格设定,因为样式会很多。所以这里使用了多态代替判断条件。 并且使用了一个键值对,来实现享元设计模式的思路。

Excel样式操作

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(',') > -1)

{

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("参数无效");

}

}

}

具体样式的多态,我就不粘贴了。就是一个很简单的抽象类,然后集成实现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 是一个反射的方法,反射实体类中的特性,并返回属性和样式特性的键值对。这个在设置里面会有用途。

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 > 0)

{

ExcelInfoAttribute attr = objAttrs[0] as ExcelInfoAttribute;

if (attr != null)

{

_infos.Add(propInfo, attr);

}

}

}

return _infos;

}

SetExcelTitle 是设置Excel的标题。

SetTitle

private void SetExcelTitle(Dictionary<PropertyInfo, ExcelInfoAttribute> excelInfos)

{

IRow rowTitle = hssfSheet.CreateRow(0);

int _cellIndex = 0;

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++;

}

}

SetExcelContent 是设置Excel的内容

SetContent

private void SetExcelContent<T>(IEnumerable<T> list, Dictionary<PropertyInfo, ExcelInfoAttribute> _excelInfos)

{

int _rowNum = 1;

Dictionary<string, ICellStyle> cellStyleList = new Dictionary<string, ICellStyle>();

foreach (T rowItem in list)

{

int _rowCell = 0;

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++;

}

}

这里就是使用键值对的属性,来获取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, 0, 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

.NET开发工具之Excel导出公共类的更多相关文章

  1. 偷懒小工具 - Excel导出公共类

    说明 最近接了一个任务,就是做一个列表的Excel导出功能.并且有很多页面都会使用这个功能. 导出的Excel大体格式如图 很简单的列表,标题加背景色,然后不同类型,显示方式不一样.对齐方式不一样.不 ...

  2. Excel导出公共函数

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

  3. 【开发工具】- 如何导出/导入Idea的配置文件

    导出配置 打开工具,找到 file -> export setting ,选择路径即可,导出的是setting.jar文件. 导入配置 file –> import setttings – ...

  4. Excel 导出通用类

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

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

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

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

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

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

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

  8. 自己写的java excel导出工具类

    最近项目要用到excel导出功能,之前也写过类似的代码.因为这次项目中多次用到excel导出.这次长了记性整理了一下 分享给大伙 欢迎一起讨论 生成excel的主工具类: public class E ...

  9. 基于jdk1.7实现的excel导出工具类

    通用excel导出工具类,基于泛型.反射.hashmap 以及基于泛型.反射.bean两种方式 import java.io.*;import java.lang.reflect.Field;impo ...

随机推荐

  1. sql执行顺序

    SQL 不同于与其他编程语言的最明显特征是处理代码的顺序.在大数编程语言中,代码按编码顺序被处理,但是在SQL语言中,第一个被处理的子句是FROM子句,尽管SELECT语句第一个出现,但是几乎总是最后 ...

  2. 跟着百度学PHP[4]-OOP面对对象编程-2-属性和方法

    简单的说 变量就是成员属性函数就是成员方法(方法有三:构造方法[即为__construct].成员方法.析构方法[__destruct]) 成员方法和成员属性都是可以加修饰词.比如封装性的方法或者属性 ...

  3. XSS Filter绕过

    之前挖到某金融网站的xss 但是困于xss filter无奈不好下手.只能在火狐下弹窗. 以下该图是我的测试. 后来发给一个Invoker哥们儿.成功给我发来payload成功绕过了XSS Filte ...

  4. C++关键字:mutable(转)

    参考:http://blog.csdn.net/hxz_qlh/article/details/14475573 修饰成员变量,在const成员函数中可修改它,在C++中还从未用过这个关键字.

  5. 编码(Code)

    很遗憾,直接搜索Code或者编码是很难得找到这本书的,我也是无意中才接触到本书. 第一次读本书,对各方面的知识都不算很懂,觉得很多地方都写的太多浅显,好像本该就是这样子,一个编码系统说的那么麻烦干嘛, ...

  6. BZOJ 4531: [Bjoi2014]路径

    Description 一个无向图,每个节点有一个字符,问形成长度为k的的合法表达式的方案数. Sol DP. \(f[i][o][p][0/1]\) 表示走 \(i\) 步,到 \(o\) ,有 \ ...

  7. sone2(未完成)

    先留个半完成代码 边看论文边看题解写的...难受.. #include<cstdio> #include<cstring> namespace utils{ inline in ...

  8. Javascript——Context和Scope的一些学习总结

    1.1.1 摘要 在我们学习Javascript过程中,常常会遇到作用域(Scope)和执行上下文(Context)等概念.其中,执行上下文与this关键字的关系密切. 有面向对象编程经验的各位,对于 ...

  9. JavaScript——this关键字

    请看下面的代码,最后alert出来的是什么呢? 1 var name = "Bob"; 2 var nameObj ={ 3 name : "Tom", 4 s ...

  10. yum install 安装时报yum doesn't have enough cached data to continue.

    yum install 安装时报yum doesn't have enough cached data to continue. 安装epel,yum -y install epel-release后 ...