C# 实现NPOI的Excel导出
技术点:
1.自定义
attribute
属性
2.通过反射取类及其属性的attribute
属性值
3.NPOI包常用属性及方法(我也仅仅知道用到过的,陌生的要么见名知意,要么百度查)
实现功能点:
List类对象的模板导出,实用场景例子见最后代码块
(emm...还是比较抽象,代码见)
EXCEL导出类DTO超类
public abstract class ExcelSuper
{
// 所有excel导出类DTO必须要继承的方法
// 限制比较弱,主要还是用来区分DTO用在何处
}
定义继承导出类DTO的特性说明类
/// <summary>
/// 导出类 类特性
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class ExcelExpClassAttribute : Attribute
{
public ExcelExpClassAttribute(int colSplit, int rowSplit, int leftmostColumn, int topRow)
{
this.colSplit = colSplit;
this.rowSplit = rowSplit;
this.leftmostColumn = leftmostColumn;
this.topRow = topRow;
}
/// <summary>
/// 冻结的列数
/// </summary>
public int colSplit { get; set; }
/// <summary>
/// 冻结的行数
/// 只冻结列时为0
/// </summary>
public int rowSplit { get; set; }
/// <summary>
/// 右边区域可见的首列序号,从1开始计算
/// </summary>
public int leftmostColumn { get; set; }
/// <summary>
/// 边区域可见的首行序号,从1开始计算,
/// 只冻结列时为0
/// </summary>
public int topRow { get; set; }
}
/// <summary>
/// 导出类 属性特性
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ExcelExpAttribute : Attribute
{
/// <summary>
/// 是否隐藏,与SortIndex搭配使用
/// </summary>
public bool IsHidden { get; set; } = false;
/// <summary>
/// 排序索引(保持连贯性、不可重复)
/// - 对应dataTable的列排序 [0,1...]
/// - 对应Excel的列索引 [0,1...]
/// </summary>
public int SortIndex { get; set; }
/// <summary>
/// Excel列名
/// </summary>
public string ColName { get; set; }
/// <summary>
/// 是否动态列
/// </summary>
public bool IsdynamicColName { get; set; }
/// <summary>
/// 是否合并行 -- 预留,后补
/// </summary>
public bool IsRowMerge { get; set; } = false;
/// <summary>
/// 合并行依据 -- 预留,后补
/// </summary>
public string RowMergeBasis { get; set; }
}
Excel帮助类
/// <summary>
/// Excel帮助类
/// </summary>
public static class ExcelHelper
{
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="workbook"></param>
/// <param name="dtSource"></param>
/// <param name="sheetNum"></param>
/// <param name="useAttributeColName"> 添加一行列名,取类自定义属性列名称:ExcelExpAttribute.ColName</param>
/// <returns></returns>
private static IWorkbook OutputExcel<T>(this IWorkbook workbook, IEnumerable<T> dtSource, int sheetNum, bool useAttributeColName = false)
{
// 读取sheet
ISheet sheet = workbook.GetSheetAt(sheetNum);
int rowIndex = sheet.LastRowNum + 1;// 获取写入行初始值
if (useAttributeColName)
{
// 待补充
}
Type objType = typeof(T);
// 取类上的自定义特性
object[] objs = objType.GetCustomAttributes(typeof(ExcelExpClassAttribute), true);
foreach (object obj in objs)
{
ExcelExpClassAttribute attr = obj as ExcelExpClassAttribute;
if (attr != null)
{
sheet.CreateFreezePane(attr.colSplit, attr.rowSplit, attr.leftmostColumn, attr.topRow);// 设置冻结行、列
break;
}
}
// 循环添加数据
foreach (T item in dtSource)
{
IRow rowi = sheet.CreateRow(rowIndex);
// 设置自适应宽度,9为Excel列数,根据需要自已修改
for (int columnNum = 0; columnNum <= rowi.LastCellNum; columnNum++)
{
int columnWidth = sheet.GetColumnWidth(columnNum) / 256;
sheet.SetColumnWidth(columnNum, columnWidth * 256);
}
// 取属性上的自定义特性
foreach (PropertyInfo propInfo in objType.GetProperties())
{
object[] objAttrs = propInfo.GetCustomAttributes(typeof(ExcelExpAttribute), true);
if (objAttrs.Length > 0)
{
ExcelExpAttribute attr = objAttrs[0] as ExcelExpAttribute;
if (attr != null)
{
if (attr.IsHidden)
continue;
int colIndex = attr.SortIndex;
var name = propInfo.Name;// 实例名称
var value = propInfo.GetValue(item);// 实例值
#region 判断值类型并填充
var newCell = rowi.CreateCell(colIndex);
switch (propInfo.PropertyType.ToString())
{
case "System.String"://字符串类型
newCell.SetCellValue(value == null ? "" : value.ToString());
break;
case "System.DateTime"://日期类型
DateTime.TryParse(value.ToString(), out DateTime dateV);
newCell.SetCellValue(dateV);
break;
case "System.Boolean"://布尔型
bool.TryParse(value.ToString(), out bool boolV);
newCell.SetCellValue(boolV);
break;
case "System.Int16"://整型
case "System.Int32":
case "System.Int64":
case "System.Byte":
int.TryParse(value.ToString(), out int intV);
newCell.SetCellValue(intV);
break;
case "System.Decimal"://浮点型
case "System.Double":
double.TryParse(value.ToString(), out double doubV);
newCell.SetCellValue(doubV);
break;
case "System.DBNull"://空值处理
newCell.SetCellValue("");
break;
default:
newCell.SetCellValue("");
break;
}
#endregion
}
}
}
rowIndex++;
}
return workbook;
}
/// <summary>
/// 导出模板Excel
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="iEnumerable">数据源</param>
/// <param name="fileFullPath">文件全路径(包含文件及后缀名)</param>
/// <returns>文件流</returns>
public static MemoryStream ExportToExcel<T>(IEnumerable<T> iEnumerable, string fileFullPath)
where T : ExcelSuper
{
// 打开模板文件并写入
var workbook = GetIWorkbook(fileFullPath, out ExcelTypeEnum type);
if (type == ExcelTypeEnum.XLS)
{
workbook = (HSSFWorkbook)workbook.OutputExcel(iEnumerable, 0, false);
}
else
{
workbook = (XSSFWorkbook)workbook.OutputExcel(iEnumerable, 0, false);
}
try
{
using (MemoryStream ms = new MemoryStream())
{
workbook.Write(ms);
ms.Flush();
ms.Position = 0;
// sheet.Dispose();
// workbook.Dispose();//一般只用写这一个就OK了,他会遍历并释放所有资源,但当前版本有问题所以只释放sheet
return ms;
}
}
catch (Exception ex)
{
throw ex;
}
}
#region Private method
private static IWorkbook GetIWorkbook(string fileFullPath, out ExcelTypeEnum type)
{
string filename = Path.GetFileNameWithoutExtension(fileFullPath);// 文件名称
string extension = Path.GetExtension(fileFullPath);// 后缀名 带点(.)
try
{
if (!File.Exists(fileFullPath))
{
throw new Exception($"模板:{filename + extension}不存在");
}
FileStream fs = new FileStream(fileFullPath, FileMode.Open, FileAccess.ReadWrite);
if (".xls".Equals(extension.ToLower()))
{
type = ExcelTypeEnum.XLS;
return new HSSFWorkbook(fs);// Excel2003以前版本
}
else
{
type = ExcelTypeEnum.XLSX;
return new XSSFWorkbook(fs);// Excel2007后的版本
}
}
catch (Exception ex)
{
throw new Exception("获取文件错误:" + ex);
}
}
#endregion
enum ExcelTypeEnum
{
XLS,
XLSX
}
}
这部分要讲的点其实挺多的,关键就是EXCEL导出所用到的数据源是强类型的。
可以看出来list
其实是EF的Queryable
toList()
后的类集合,作为数据源存在;// 比较喜欢强类型,直接点出来的属性让人放心ヾ(•ω•`)o里面的DTO
DesWeeklyReportExcExp
继承ExcelSuper
,特性分别加在类及属性上。
public class XXXXController : CoreController
{
// 控制器内部
[HttpPost]
public ActionResult export()
{
// 控制器接口
var list = op
.GetPagedQuery(PageModel)
.Select(s => new DesWeeklyReportExcExp
{
col1 = s.Project.ProjName,
col2 = s.ColAttROPDate1?.ToString("yyyy.MM.dd"),
col3 = (s.ColAttROPDate2 == null ? "无" : s.ColAttROPDate2.Value.ToString("yyyy.MM.dd"))
+ "/"
+ (s.ColAttROPDate3 == null ? "无" : s.ColAttROPDate3.Value.ToString("yyyy.MM.dd")),
col4 = s.ColAttROPDate4?.ToString("yyyy.MM.dd")
}).ToList();
string filePath = Server.MapPath("~/download/[这是模板名称].xlsx");
string filename = Path.GetFileNameWithoutExtension(filePath);// 文件名称
string extension = Path.GetExtension(filePath);// 后缀名 带点(.)
string fileDownloadName = filename + extension;
var fs = ExcelHelper.ExportToExcel(list, filePath).ToArray();
return File(fs, "application/ms-excel", fileDownloadName);
}
}
[ExcelExpClassAttribute(2, 0, 2, 0)]
public class DesWeeklyReportExcExp : ExcelSuper
{
/// <summary>
/// 列1
/// </summary>
[ExcelExp(SortIndex = 0, ColName = "列1")]
public string col1 { get; set; }
/// <summary>
/// 列2
/// </summary>
[ExcelExp(SortIndex = 0, ColName = "列2")]
public string col2 { get; set; }
/// <summary>
/// 列3
/// </summary>
[ExcelExp(SortIndex = 0, ColName = "列3")]
public string col3 { get; set; }
/// <summary>
/// 列4
/// </summary>
[ExcelExp(SortIndex = 0, ColName = "列4")]
public string col4 { get; set; }
}
部分拙见,大部分还需要补充,比如设置合并列,比如数据源支持DataTable导出等等,还有现有的代码可能不够完善,看到的多多提下宝贵意见吧
C# 实现NPOI的Excel导出的更多相关文章
- 并发编程概述 委托(delegate) 事件(event) .net core 2.0 event bus 一个简单的基于内存事件总线实现 .net core 基于NPOI 的excel导出类,支持自定义导出哪些字段 基于Ace Admin 的菜单栏实现 第五节:SignalR大杂烩(与MVC融合、全局的几个配置、跨域的应用、C/S程序充当Client和Server)
并发编程概述 前言 说实话,在我软件开发的头两年几乎不考虑并发编程,请求与响应把业务逻辑尽快完成一个星期的任务能两天完成绝不拖三天(剩下时间各种浪),根本不会考虑性能问题(能接受范围内).但随着工 ...
- C#中用NPOI的excel导出
//机构表导出 private static List<User2> amininf = new BLL.Bll().GetUser2s(); //定义数据源导出对象 #region 导出 ...
- PowerBuilder中调用NPOI进行Excel导出格式设置示例
// 功能 :新建excel带边框的单元格,格式为数字并显示为美元货币 // 参数 :ai_row,行号:ai_col,列号 // 返回值 :true/false // 作者 :潮崖之飔 // 日期 ...
- .NET Excel导出方法及其常见问题详解
摘要:.NET Excel导出方法及其常见问题详解. 一.Excel导出的实现方法 在.net 程序开发中,对于Excel文件的导出我们一共有三种导出方式: 利用文件输出流进行读写操作 这种方式的导出 ...
- NPOI操作EXCEL(四)——反射机制批量导出excel文件
前面我们已经实现了反射机制进行excel表格数据的解析,既然有上传就得有下载,我们再来写一个通用的导出方法,利用反射机制实现对系统所有数据列表的筛选结果导出excel功能. 我们来构想一下这样一个画面 ...
- asp.net mvc4 easyui datagrid 增删改查分页 导出 先上传后导入 NPOI批量导入 导出EXCEL
效果图 数据库代码 create database CardManage use CardManage create table CardManage ( ID ,) primary key, use ...
- NPOI实现Excel导入导出
NPOI实现Excel的导入导出,踩坑若干. Cyan是博主[Soar360]自2014年以来开始编写整理的工具组件,用于解决现实工作中常用且与业务逻辑无关的问题. 什么是NPOI? NPOI 是 P ...
- npoi实现数据导出Excel
npoi .NET第三方的Office功能组件. 链接地址 http://npoi.codeplex.com/ 引用命名空间 using NPOI.HSSF.UserModel; using ...
- ASP.NET Core 2.2 : 十六.扒一扒新的Endpoint路由方案 try.dot.net 的正确使用姿势 .Net NPOI 根据excel模板导出excel、直接生成excel .Net NPOI 上传excel文件、提交后台获取excel里的数据
ASP.NET Core 2.2 : 十六.扒一扒新的Endpoint路由方案 ASP.NET Core 从2.2版本开始,采用了一个新的名为Endpoint的路由方案,与原来的方案在使用上差别不 ...
随机推荐
- LuoguB2075 幂的末尾 题解
Content 求 \(a^b\) 的末三位. 数据范围:\(1\leqslant a\leqslant 100\),\(1\leqslant b\leqslant 10^4\). Solution ...
- AT1381 エンド・オブ・ビギニング 题解
Content 有 \(n\) 组询问,每组询问给定三个字符串 \(s_1,s_2,s_3\). 如果 \(s_1\) 是 BEGINNING,输出 \(s_3\) 的第一个字符. 如果 \(s_1\ ...
- 简单备忘一下Linux下的wget和curl如何使用http proxy
简单备忘一下Linux下的wget和curl如何使用http proxywget -e "http_proxy=porxyhost:port" www.baidu.comcurl ...
- HTML body体
p br h div span <!DOCTYPE html> <html lang="en"> <head> <meta charset ...
- 实体转为json的,如何处理外键情况
实体转为json的,如何处理外键情况 jc.registerJsonValueProcessor(Userrelation.class, new JsonValueProcessor() {// 此处 ...
- js控制滚动条在最底部位置
window.scrollTo(0, document.body.scrollHeight) 如果需要始终保持在最底部,可以循环调用该方法 如果是div的 /*滚动条到地步*/ function to ...
- 【LeetCode】865. Smallest Subtree with all the Deepest Nodes 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...
- hdu-1593 find a way to escape(贪心,数学)
思路:两个人都要选取最优的策略. 先求外层那个人的角速度,因为他的角速度是确定的,再求内层人的当角速度和外层人一样时的对应的圆的半径r1.外层圆的半径为d; 那么如果r1>=外围圆的半径,那么肯 ...
- WebRTC源码开发(一)MacOS下源码下载、编译及Demo运行
工作需要测试网络传输算法,逐学习WebRTC源码 工作环境 Mac OS 10.14 Xcode 10.2.1 源码下载 从google(需要[你懂的]) 首先[你懂的] 打开终端,输入curl ww ...
- 新手入门typeScript
强类型与弱类型(类型安全) 强类型不允许随意的隐士类型转换,而弱类型是允许的 变量类型允许随时改变的特点,不是强弱类型的差异 静态类型与动态类型(类型检查) 静态类型:一个变量声明时它的类型就是明确的 ...