DataTable的行列转换及多表头HTML表格转Excel
需要解决的问题:
1、根据数据库中多个不同字段名称的以行形式表现的数据转为以列形式展现的数据;
2、HTML多表头数据展示,做到数据“一个萝卜一个坑”,并求出多条数据的合计数据。
3、将HTML表格转为EXCEL。
先看效果图:
图1 HTML页面表格
图2 导出为Excel文档
图3 数据行不同,表头列数也不相同
图4 行数据记录不同时,表格的表头也变得不同(对比图1),各相关行数据需要一一对应。
HTML表格的结构:
<table id="TableStatics" align="center">
<tbody>
<tr>
<td rowspan="2">
工单编号
</td>
<td rowspan="2">
生产数量
</td>
<td colspan="7" class="Ocv">
OCV1
</td>
<td colspan="6" class="Ocv">
OCV2
</td>
<td colspan="7" class="Ocv">
OCV3
</td>
<td colspan="9" class="Ocv">
OCV4
</td>
</tr>
<tr>
<td nowrap="">
A
</td>
<td nowrap="">
A1
</td>
<td nowrap="">
电压B
</td>
<td nowrap="">
电压C
</td>
<td nowrap="">
内阻D
</td>
<td nowrap="">
扫描异常
</td>
<td nowrap="">
数码A
</td>
<td nowrap="">
A
</td>
<td nowrap="">
C
</td>
<td nowrap="">
D1
</td>
<td nowrap="">
D3
</td>
<td nowrap="">
N2
</td>
<td nowrap="">
扫描异常
</td>
<td nowrap="">
A
</td>
<td nowrap="">
C
</td>
....
<td nowrap="">
扫描异常
</td>
</tr>
<!-- 数据行开始 -->
<tr>
<td>
20170221
</td>
<td>
100000
</td>
<td>
18
</td>
...
</tr>
<tr.....>
</tbody>
</table>
//ExcelCellInfo.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace LEBLL.ExcelOutput
{
/// <summary>
/// Excel单元格信息
/// </summary>
public class ExcelCellInfo
{
/// <summary>
/// 起始位置:X方向
/// </summary>
public int StartX = 1;
/// <summary>
/// 起始位置:Y方向
/// </summary>
public int StartY = 1;
/// <summary>
/// 单元格横向格数(占几列)
/// </summary>
public int ColSpan = 1;
/// <summary>
/// 单元格竖向格数(占几行)
/// </summary>
public int RowSpan = 1;
/// <summary>
/// 单元格内容
/// </summary>
public string Content = string.Empty;
}
}
//ExcelGenerator.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using HtmlAgilityPack;
namespace LEBLL.ExcelOutput
{
public class ExcelGenerator
{
private string excelFileName = "Excel";
private int sheetCount = 1;
ExcelTableInfo excelTableInfo;
HtmlNode htmlNodeTable;
public string TableID = "TableStatics";
/// <summary>
///
/// </summary>
/// <param name="tableHTML">表格的HTML代码</param>
public ExcelGenerator(string tableHTML)
{
//Excel文件名
this.excelFileName = "电池批次统计";
//Excel文件的sheet数量
this.sheetCount = 1;
excelTableInfo = new ExcelTableInfo();
excelTableInfo.TableName = "电池批次统计信息";
excelTableInfo.SheetIndex = 1;
excelTableInfo.StartX = 1;
excelTableInfo.StartY = 1;
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(tableHTML);
htmlNodeTable = htmlDoc.DocumentNode.SelectSingleNode("//table[@id='"+ TableID + "']");
excelTableInfo.HtmlNodeOfTable = htmlNodeTable;
}
public string Save(string fileDirectory)
{
ExcelWriter excelWriter = new ExcelWriter(this.sheetCount);
List<ExcelCellInfo> cells = this.ConvertToCells(excelTableInfo.StartX, excelTableInfo.StartY + 1, htmlNodeTable);
if (null != cells && cells.Count > 0)
{
excelWriter.Write(excelTableInfo.TableName, excelTableInfo.SheetIndex, cells);
}
string filePhysicalPath = System.Web.HttpContext.Current.Server.MapPath(fileDirectory);
string excelName = Utility.GetUniqueFileName(filePhysicalPath, this.excelFileName + ".xls");
excelWriter.Save(filePhysicalPath, excelName);
return excelName;
}
private List<ExcelCellInfo> ConvertToCells(int pnStartX, int pnStartY, HtmlNode htmlNodeOfTable)
{
HtmlNodeCollection trNodes = htmlNodeOfTable.SelectNodes("tr");
List<ExcelCellInfo> listCells = new List<ExcelCellInfo>();
for (int y = 0; y < trNodes.Count; y++)
{
HtmlNodeCollection tdNodes = trNodes[y].SelectNodes("td");
for (int x = 0; x < tdNodes.Count; x++)
{
ExcelCellInfo newCell = new ExcelCellInfo();
newCell.StartY = pnStartY + y;
newCell.StartX = pnStartX + this.HorizontalDeduction(tdNodes[x]);
newCell.ColSpan = tdNodes[x].GetAttributeValue("colspan", 1);
newCell.RowSpan = tdNodes[x].GetAttributeValue("rowspan", 1);
string content = tdNodes[x].InnerText.Trim();
newCell.Content = ((x == 0) ? "'" + content : content);
listCells.Add(newCell);
}
}
this.VerticalDeduction(listCells);
return listCells;
}
/// <summary>
/// 第一次colspan推演,即横向
/// </summary>
/// <param name="htmlNodeOfTd"></param>
/// <returns></returns>
private int HorizontalDeduction(HtmlNode htmlNodeOfTd)
{
HtmlNode htmlNodPreSibling = htmlNodeOfTd.PreviousSibling;
while (htmlNodPreSibling != null && htmlNodPreSibling.Name != htmlNodeOfTd.Name)
{
htmlNodPreSibling = htmlNodPreSibling.PreviousSibling;
}
if (htmlNodPreSibling != null)
{
int colSpan = htmlNodPreSibling.GetAttributeValue("colspan", 1);
return HorizontalDeduction(htmlNodPreSibling) + colSpan;
}
return 0;
}
/// <summary>
/// 第二次推演,即纵向
/// </summary>
/// <param name="listCells"></param>
private void VerticalDeduction(List<ExcelCellInfo> listCells)
{
for (int i = 0; i < listCells.Count; i++)
{
ExcelCellInfo currentCell = listCells[i];
bool bActedPush = false;
do
{
int comparedIndex = -1;
for (int j = i - 1; j >= 0; j--)
{
if (listCells[j].StartX == currentCell.StartX)
{
comparedIndex = j;
break;
}
}
if (comparedIndex >= 0)
{
if (listCells[comparedIndex].RowSpan > (currentCell.StartY - listCells[comparedIndex].StartY))
{
currentCell.StartX += listCells[comparedIndex].ColSpan;
bActedPush = true;
for (int k = i + 1; k < listCells.Count; k++)
{
if (listCells[k].StartY == currentCell.StartY)
{
listCells[k].StartX += listCells[comparedIndex].ColSpan;
}
}
}
else
{
bActedPush = false;
}
}
else
{
bActedPush = false;
}
}
while (bActedPush);
}
}
}
}
//Utility.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
namespace LEBLL.ExcelOutput
{
public class Utility
{
/// <summary>
/// 在指定目录以指定文件名为基础,取得系列文件名。
/// 如果已存在指定文件,则生成: path\fileName_(当前数字+1).后缀名。
/// </summary>
/// <param name="path">指定目录</param>
/// <param name="fileName">指定文件名</param>
/// <returns>生成: path\fileName_(当前数字+1).后缀名</returns>
public static string GetUniqueFileName(string path, string fileName)
{
string file = fileName;
string ext = null;
int n = 1;
int lastDotIndex = 0;
lastDotIndex = fileName.LastIndexOf(".");
if (lastDotIndex == -1) {
ext = "";
} else {
ext = file.Substring(lastDotIndex);
file = file.Substring(0, lastDotIndex);
}
string fileOrg = file;
while (System.IO.File.Exists(path + "\\" + file + ext)) {
file = fileOrg + "_" + n;
n = n + 1;
}
return file + ext;
}
/// <summary>
/// 下载文件
/// </summary>
/// <param name="path"></param>
/// <param name="fileName"></param>
/// <param name="fileContentType"></param>
public static void DownLoadFile(string path, string fileName, string fileContentType)
{
string absolutePath = System.Web.HttpContext.Current.Server.MapPath(path);
FileInfo fi = new FileInfo(absolutePath + "\\" + fileName);
HttpResponse contextResponse = HttpContext.Current.Response;
contextResponse.Clear();
contextResponse.Buffer = true;
contextResponse.Charset = "UTF-8";
contextResponse.AppendHeader("Content-Disposition", String.Format("attachment;filename={0}", fileName)); //定义输出文件和文件名
contextResponse.AppendHeader("Content-Length", fi.Length.ToString());
contextResponse.ContentEncoding = Encoding.UTF8;
contextResponse.ContentType = fileContentType;
contextResponse.WriteFile(fi.FullName);
contextResponse.Flush();
contextResponse.End();
}
}
}
//ExcelTableInfo.cs
using System;
using System.Collections.Generic;
using System.Text;
using HtmlAgilityPack;
namespace LEBLL.ExcelOutput
{
public class ExcelTableInfo
{
public string TableName = string.Empty;
public int SheetIndex = 1;
public int StartX = 1;
public int StartY = 1;
public HtmlNode HtmlNodeOfTable = null;
}
}
// ExcelWriter.cs
using System;
using System.Collections.Generic;
using System.Text;
using HtmlAgilityPack;
using Microsoft.Office.Interop.Excel;
using System.Reflection;
using System.Drawing;
namespace LEBLL.ExcelOutput
{
public class ExcelWriter
{
private Application _excelApp = null;
private Workbook _excelWorkbook = null;
private object _missingV = Missing.Value;
public ExcelWriter(int sheetCount)
{
this._excelApp = new Application();
this._excelApp.SheetsInNewWorkbook = sheetCount;
this._excelWorkbook = this._excelApp.Workbooks.Add(this._missingV);
}
public void Write(string tableName,int pnAtWhickSheet, List<ExcelCellInfo> listCells)
{
Worksheet currentSheet = this._excelWorkbook.Sheets[pnAtWhickSheet] as Worksheet;
foreach (ExcelCellInfo ec in listCells)
{
currentSheet.Cells[ec.StartY, ec.StartX] = ec.Content;
currentSheet.Range[currentSheet.Cells[ec.StartY, ec.StartX], currentSheet.Cells[ec.StartY + ec.RowSpan - 1, ec.StartX + ec.ColSpan - 1]].Merge(this._missingV);
}
//表区域边框颜色
Range rangeTable = currentSheet.Range[currentSheet.Cells[listCells[0].StartY, listCells[0].StartX], currentSheet.Cells[listCells[listCells.Count - 1].StartY + listCells[listCells.Count - 1].RowSpan - 1, listCells[listCells.Count - 1].StartX + listCells[listCells.Count - 1].ColSpan - 1]];
rangeTable.Borders.Color = ColorTranslator.ToOle(Color.Black);
//表名称
currentSheet.Cells[listCells[0].StartY - 1, listCells[0].StartX] = tableName;
Range rangeTableTitle = currentSheet.Range[currentSheet.Cells[listCells[0].StartY - 1, listCells[0].StartX], currentSheet.Cells[listCells[0].StartY - 1, listCells[listCells.Count - 1].StartX + listCells[listCells.Count - 1].ColSpan - 1]];
rangeTableTitle.Merge(this._missingV);
rangeTableTitle.Font.Bold = true;
rangeTableTitle.HorizontalAlignment = XlVAlign.xlVAlignCenter;
}
/// <summary>
/// 保存Excel到指定目录下的指定文件中
/// </summary>
/// <param name="filePhysicalPath">指定目录</param>
/// <param name="excelFileName">Excel文件名</param>
public void Save(string filePhysicalPath, string excelFileName)
{
try
{
this._excelWorkbook.RefreshAll();
this._excelWorkbook.SaveAs(filePhysicalPath + "\\" + excelFileName, this._missingV, this._missingV, this._missingV, this._missingV, this._missingV, XlSaveAsAccessMode.xlNoChange, this._missingV, this._missingV, this._missingV, this._missingV, this._missingV);
}
catch (Exception ex)
{
}
finally
{
this._excelWorkbook.Close(false, this._missingV, this._missingV);
this._excelApp.Quit();
this._excelApp = null;
GC.Collect();
}
}
}
}
//调用时:
private void BuildTable()
{
SetDefaultStartEndTime();
TableExporter tableExporter = new TableExporter();
tableExporter.TableID = tableID;
string htmlTable = tableExporter.BuildTable(this.OrderNo, this.StartTime, this.EndTime);
PanelTable.Html = htmlTable;
TextField_TableHtml.Text = Server.HtmlEncode(htmlTable);
string resultTimeCost = string.Format("{0},总耗时:{1}。", "产生数据", tableExporter.TimeCost);
Label_TimeCost.Text = resultTimeCost;
}
protected void ExportExcel(object sender, EventArgs e)
{
string directory = "~/ExcelFile";
// 表格的HTML代码
string tableHTML = Server.HtmlDecode(TextField_TableHtml.Text);
ExcelGenerator excelGenerator = new ExcelGenerator(tableHTML);
excelGenerator.TableID = tableID;
string excelName = excelGenerator.Save(directory);
if (excelName != null)
{
Utility.DownLoadFile(directory, excelName, "application/ms-excel");
}
}
(写作中,未完待续)
DataTable的行列转换及多表头HTML表格转Excel相关推荐
- element 树形表格行列转换(行列转换系列2)
我们上篇文章写的是关于element普通表格的行列转换,下面我们开始写element树形表格的行列转换 实现效果: 首先:我们通过element的官方文档中可以看出.当row 字段中包含childre ...
- 金仓数据库KingbaseES行列转换
概述 行列转换是在数据分析中经常用到的一项功能,金仓数据库KingbaseES从V8R6C3B0071版本开始通过扩展插件(kdb_utils_function)支持pivot和unpivot功能.在 ...
- element 普通表格行列转换(行列转换系列1)
因为用户的使用习惯.最近小白新完成的一个系统,里面使用的element的表格基本都是行列的表格,因此就想着整理一下,其中包括了,后端应该给的数组格式,以及前端如何渲染 这就是渲染后的样式 首先在ele ...
- [转载]SQL Server行列转换实现
一.Pivot和UnPivot介绍 1.Pivot介绍 PIVOT用于将列值旋转为列名(即行转列),在SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT的一般语法是:PIVO ...
- SQL Server 行列转换(2)
参考前一个例子http://www.cnblogs.com/insus/articles/1969896.html,现想使用另外一种方式来处理行列转换,实现下面效果: 参考代码: View Code ...
- Oracle 行列转换总结
行列转换包括以下六种情况: *列转行 *行转列 *多列转换成字符串 *多行转换成字符串 *字符串转换成多列 *字符串转换成多行 下面分别进行举例介绍. 首先声明一点,有些例子需要如下10g及以后才有的 ...
- SAP行列转换的一个方法
经常会遇到行列转换的需求.于是就找到了这样的一段代码.虽然也有其他的转换方法.但是都没有及时的记录.于是 就想起了将这段经典代码写在这里. TABLES spfli. data: it_dat ...
- SQL语句行列转换两种方法 case ...when 和pivot函数应用
2019独角兽企业重金招聘Python工程师标准>>> SQL语句行列转换两种方法 case ...when 和pivot函数应用SQL语句行列转换两种方法 case ...when ...
- DataStage系列教程 (Pivot_Enterprise 行列转换)
有人提到Pivot_Enterprise这个组件,之前没有用过,今天捣腾了会,写下来供以后参考,如果有什么不对的,还请多指出,谢谢! Pivot_Enterprise主要用来进行行列转换. 1 示例 ...
最新文章
- 通过钉钉群聊机器人推送zabbix告警
- 安卓开源项目周报1220
- 设计模式之间的关联关系和对比
- android程序的入口点,常见android面试基础题
- 我的笔记本的鼠标又乱跑了!寻求帮助!
- POS机刷卡机招商加盟企业网站源码
- nginx(4、缓存)
- AngularJs学习笔记(二)
- 夜神安卓模拟器安装xposed框架
- Python求梅森尼数
- WEB开发新势力——Openparty
- 190204每日一句
- Spring AOP原理分析(四)--AnnotationAwareAspectJAutoProxyCreator#postProcessBeforeInstantiation源码解析
- 解决安装VC2015失败的问题
- windos读写ext3工具_“ ext2fsd” Windows系统工具,用于读写ext2 / 3/4文件系统
- 网站域名如何接入腾讯云CDN业务详细步骤!
- 查找网络计算机步骤,如何查找到局域网中指定IP地址的是哪一台电脑
- 学生用计算机的感叹号在哪,感叹号怎么打电脑(感叹号的用法及举例)
- PWmat案例赏析:计算精度高、速度快的第一性原理计算,研究表面终端结构对NV色心影响
- Oracle-SQL语句的逻辑读怎么计算