Bootstrap

html table to excel 然后打印,html table to Excel(1)

转载连接:http://www.fengfly.com/plus/view-197341-1.html

最近做一个小的报表系统,功能本身没什么。最后客户要求一个打印功能,所谓打印,就是按照页面上报表的样子,一模一样的为其生成Excel文件。

再也不想为了构造结构一样的Excel表格而再次考虑繁琐数据逻辑了!于是乎冒出了这样的一个想法:我要是能获得页面上的报表table,那么只要分析其结构,不就可以构造出相应的Excel表格来吗?

思来想去,觉得这应该是一条可以走的通的路,于是便着手寻找实现的办法。终于,发现WebRequest和WebResponse,其分别代表一个Web请求和服务器基于请求的回应,而这个回应包含服务器返回的数据流,从数据流便可获取想要的报表table。

整个Excel生成功能的思路如下图所示:

700b80f7825e720f71d98c45f0a245e2.png

涉及WebRequest、WebResponse的核心代码如下:

1

WebRequest wr=

WebRequest.Create(psUrl);

2

HttpWebRequest oRequest

=

wr

as

HttpWebRequest;

3

if

(

null

==

oRequest)

return

null

;

4

5

HttpWebResponse oResponse  ;

=

(HttpWebResponse) oRequest.GetResponse();

6

string

sResponseContent

=

string

.Empty;

7

if

(oResponse.StatusCode

==

HttpStatusCode.OK)

8

{

9

using

(StreamReader sr

=

new

StreamReader(oResponse.GetResponseStream(),Encoding.UTF8))

10

{

11

sResponseContent

=

sr.ReadToEnd();

//

sResponseContent,保存了服务器返回的数据流字符串内容

12

sr.Close();

13

}

14

}

15

oResponse.Close();

特别需要说明的是,在从数据流向字符串转化的过程中,其内会残留许多\r\t之类的转义字符,我们需要将其剔除。

1

sResponseContent

=

Regex.Replace (sResponseContent,

"

[\n\r\t]

"

,

""

);

本篇至此,下篇将着重介绍table到Excel cell的转化过程。

在上一篇文章中,已经解决了如何获取html table结构的问题。在本篇文章中,我们着力于table结构的解析。

Html table的结构我们大家都很熟悉,那么在另一端如何构造一个结构,让Excel可以很好的接受和处理呢?

直观的看,一个完整Excel的内容是由位于各个单元格(Cell)中的内容组合而成的。而每个单元格(Cell)都有相应X、Y坐标来标示其位置。也就是说,一个Excel文件实质上就是许多Cell构成的集合,每个Cell用坐标属性确定位置,用内容属性存储内容。

基于此,我设计了最基本的Cell结构:

◆ X坐标

◆ Y坐标

◆ 合并列情况

◆ 合并行情况

◆ 内容

构成Excel的最基本的结构已经确定,下一步摆在我们面前的就是将html table转化为Excel Cell集合。

Html table中的每个td节点对应一个Excel单元格,其内容不必说,行、列的合并情况也自可由td的rowspan、colspan属性得出,转化的关键点就在于由table的tr td结构定位Excel单元格位置,即X、Y坐标。

Y坐标容易确定,即td所在tr的行数。至于一td的X坐标,其要受到两方面因素的影响:与该td同处一tr,但位于其之前(反映在表格视觉上即其左侧td)td的占位情况和该td所在tr的之前tr中某些td的跨行情况。

基于此种考虑,定位td的X坐标需经过两个过程的推导:用于处理左侧td占位影响的横向推导(Horizontal Deduction)和处理之前行跨行td影响的纵向推导(Vertical Deduction)。

以下图所示table为例,展示两次推导过程。

acf3176753cde15218fb6f5ef76cf974.png

横向推导(Horizontal Deduction)  一次横向推导(Horizontal Deduction)限定在一tr范围内。整个过程基于递归的原理,递归模型如下:

b3e1038d36cd6a683f9b2ff7af34562b.png

核心代码为:

1

private

int

HorizontalDeduction(HtmlNode phnTd)

2

{

3

HtmlNode hnPreviousSibling

=

phnTd.PreviousSibling;

4

while

(hnPreviousSibling

!=

null

&&

hnPreviousSibling.Name

!=

phnTd.Name)

5

{

6

hnPreviousSibling

=

hnPreviousSibling.PreviousSibling;

7

}

8

9

if

(hnPreviousSibling

!=

null

)

10

{

11

int

nColSpan

=

hnPreviousSibling.GetAttributeValue(

"

colspan

"

,

1

);

12

return

HorizontalDeduction(hnPreviousSibling)

+

nColSpan;

13

}

14

15

return

0

;

16

}

经过横向推导,各td的X、Y坐标如下图所示:

a030c9a795d8443186dcfa5302c4f0d8.png

纵向推导(Vertical Deduction)  一次纵向推导的过程可以描述为(当前推导td用A表示):

找到A之前的行tr中与A具有相同X坐标的td节点B

if (B.rowspan>(A.Y-B.Y))

{

X+=B.colspan,即A的X坐标向后推B.colspan的位置

同时,与A同处一tr但在其后边的td节点均应向后推B.colspan个位移

}

对td节点A反复执行这样的一个过程,直到确定A无需再次移动。

纵向推导核心代码为:

1

bool

bActedPush

=

false

;

2

3

do

4

{

5

int

nComparedItemIndex

=

-

1

;

6

for

(

int

j

=

i

-

1

; j

>=

0

; j

--

)

7

{

8

if

(plstCells[j]._nStartX

==

oCurrentCell._nStartX)

9

{

10

nComparedItemIndex

=

j;

11

break

;

12

}

13

}

14

15

if

(nComparedItemIndex

>=

0

)

16

{

17

if

(plstCells[nComparedItemIndex]._nRowSpan

>

(oCurrentCell._nStartY

-

plstCells[nComparedItemIndex]._nStartY))

18

{

19

oCurrentCell._nStartX

+=

plstCells[nComparedItemIndex]._nColSpan;

20

21

bActedPush

=

true

;

22

23

for

(

int

k

=

i

+

1

; k

<

plstCells.Count; k

++

)

24

{

25

if

(plstCells[k]._nStartY

==

oCurrentCell._nStartY)

26

{

27

plstCells[k]._nStartX

+=

plstCells[nComparedItemIndex]._nColSpan;

28

}

29

}

30

}

31

else

32

{

33

bActedPush

=

false

;

34

}

35

}

36

else

37

{

38

bActedPush

=

false

;

39

}

40

}

41

while

(bActedPush);

以示例table中的four td为例,其经过纵向推导过程后的坐标位置情况如下图:

8aa6cc9f9ca4636eb2e1d5e794db5844.png

关于示例代码的几点说明:

1、 在示例代码中,我通过一个Config文件对生成的Excel的文件名、其内报表的内容和位置做了一些控制。基本内容如下:

<

ExcelDocument

>

<

BaseInfo

>

<

FileName

>

Sample Excel File

FileName

>

<

SheetCount

>

1

SheetCount

>

BaseInfo

>

<

Tables

>

<

ExcelTable

>

<

TableName

>

示例表一

TableName

>

<

WhichSheet

>

1

WhichSheet

>

<

StartX

>

2

StartX

>

<

StartY

>

2

StartY

>

<

Source

>

Sample_Page_1.aspx

Source

>

ExcelTable

>

Tables

>

ExcelDocument

>

2、 在解析html的过程中,我使用了HtmlAgilityPack解析工具。感兴趣的朋友可以研究一下。地址是这里:http://htmlagilitypack.codeplex.com/。

3、 HtmlAgilityPack解析html的过程中,其将html标签之间的空隙也会看成是一个节点,为内容为空字符串的文本节点,这点大家应注意。

4、 该示例代码基本上是一个完整的功能,并且和系统中其他模块耦合度很小。有类似需求的朋友可以拿来直接用。

;