一次艰难的ASP.NET生成Excel文件的历程总结

原文地址为: 一次艰难的ASP.NET生成Excel文件的历程总结

统计系统的最后一项任务是导出数据到Excel,由于是第一次做,边学边搞,真费了一番周折,完成之后赶紧做了个总结

开发环境如下:

操作系统:Windows7

开发工具:vs2010

.NET版本:4.0

服务器系统:Windows2003

office版本:2003

本文主要涉及的问题:

1) 生成Excel文件的方法

2)合并单元格与数字列标识转字母列标识

3)"异常来自 HRESULT:0x800A03EC "的原因

4)"对象为空"异常和服务器端部署

5)"80080005异常"与权限配置

一.生成Exel的方法

生成Excel的方法为调用本地Office COM组件,操作Excel。新建项目后,添加对应Office版本的Microsoft.Office.Interop.Excel的引用,如图1-1所示。

ecdf14b19202300549141cc16b04beab.jpe

1-1 添加Microsoft.Office.Interop.Excel引用

为方便起见,这里以一个示例程序说明我的过程。示例程序整体结构如代码清单1-1所示。

代码清单1-1 示例程序结构

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Excel=Microsoft.Office.Interop.Excel;

namespace excel

{

class Program

{

private Excel.Application app = null;

private Excel.Workbook workbook = null;

private Excel.Worksheet worksheet = null;

private Excel.Range workSheet_range = null;

static void Main(string[] args)

{

}

public void createExcel()

{

}

public void addData(int row, int col, string data, string formatstring cell1string cell2)

{

}

public void export()

{

}

}

}

首先我先声明了四个字段,分别解释如下:

1)Excel.Application app。Application对象表示Excel应用程序本身。Application对象公开了大量有关正在运行的应用程序、应用于该实例的选项以及在该实例中打开的当前用户的对象的信息。

2)Excel.Workbook workbook。Excel.Workbook类表示Excel应用程序中的单个工作簿。

3)Excel.Worksheet worksheet。Excel提供Sheets集合作为 Microsoft.Office.Interop.Excel.Workbook 对象的属性,但是Excel中没有Sheet类。相反,Sheets 集合的每个成员都是一个Microsoft.Office.Interop.Excel.Worksheet对象,或者是一个 Microsoft.Office.Interop.Excel.Chart对象。

4)Excel.Range workSheet_range。Excel.Range对象是Excel应用程序中最常用的对象。在能够处理Excel内的任何范围之前,必须将它表示为Range对象,并处理该对象的方法和属性。Range对象表示一个单元格、一行、一列、包含一个或多个单元格块(可以连续,也可以不连续)的单元格选定范围,甚至多个工作表中的一组单元格。

createExcel()方法用来创建Excel实例,也就是初始化声明的四个变量,实现代码如代码清单1-2所示。

代码清单1-2 初始化变量

public void createExcel()

{

app = new Excel.Application();

app.Visible = true;

workbook = app.Workbooks.Add(1);//创建workbook

worksheet = (Excel.Worksheet)workbook.Sheets[1];//创建worksheet

}

如代码清单1-2,Workbook对象由Application对象创建,Worksheet对象由Workbook对象创建。

addData()方法用来向表格中添加数据,四个参数分别为行、列、数据、数据格式,范围起始格、范围结束格。实现代码如代码清单1-3所示。

代码清单1-3 添加数据

worksheet.Cells[row, col] = data;

workSheet_range = worksheet.get_Range(cell1, cell2);

workSheet_range.Borders.Color = System.Drawing.Color.Black.ToArgb();

workSheet_range.NumberFormat = format;

Excel中的每一个格以行和列组成的坐标来唯一标识,这里行标识是数字,从1到n;列标识是字母,从A到Z,从AA到AZ……。

workSheet_range对象由worksheet.get_Range(cell1, cell2)方法来初始化,从cell1到cell2会被作为一个整体处理。workSheet_range.NumberFormat设置该区域内的字符格式化方式。

export()方法用来导出生成的Excel,实现如代码清单1-4所示。

代码清单1-4 导出Excel

public void export()

{

workbook.SaveCopyAs(@"D:/aa.xls");

app.Quit();

}

Workbook对象由三个方法用来保存Excel文件,分别为Save方法、SaveCopyAs方法和SaveAs方法,这里使用SaveCopyAs方法。保存之好调用Application对象的Quit()方法释放资源。

在本地测试一切正常,测试结果如图1-2所示。

64f8ce0dc545edb600a63c35fa86b575.jpe

1-2 测试结果

二.合并单元格与数字列标识转字母列标识

合并单元格的时候,必须要传递以字母表示的列标识给Excel.Range对象的.Mergeint)方法,但是我们取数据和填充数据使用的都是数字标识,这时候必须转成如图1-3的形式。

cba4c5392462d8907ddc39886443312e.jpe

1-3 Excel的字母列标识

代码清单1-5提供的算法可解决这个问题。

代码清单1-5 数字列标识转字母列标识

public string ToName(int index)

{

if (index <= 0)

throw new Exception("invaild parameter");

index--;

List<string> chars = new List<string>();

do

{

if (chars.Count > 0)

index--;

chars.Insert(0, ((char)(index % 26 + (int)'A' )).ToString());

index = (int)((index - index % 26) / 26);

} while (index > 0);

 

return String.Join(string.Empty, chars.ToArray());

}

三. "异常来自 HRESULT:0x800A03EC "的原因

一不小心就会出"异常来自 HRESULT:0x800A03EC "的异常,原因很简单,Excel的行列都是从1开始的,如果我们习惯性的给worksheet.Cells[0, 0]或者worksheet.Cells[1, 0]或者worksheet.Cells[0, 1]赋值,就会引发该异常。

四. "未将对象引用设置到对象实例"和服务器端部署

客户端一切运行正常,接下来就是要把DLL部署到服务器上。可是服务器根本没用安装Office,在网站找了很久,也没找到此种方法的解决方案。无奈就安装了Office,本来以为万事大吉,但是每次都提示"未将对象引用设置到对象实例"。采用抛异常的方法才知道示例程序中的几个主要字段都为空,那就是本地的COM根本没调用成功。找到http://www.cnblogs.com/Mainz/archive/2009/11/11/microsoft_office_interop_excel.html文章,于是乎又进行了如下操作。

从服务器上把EXCEL.EXE拷到了本机上,然后从VS2010的命令行启动TlbImp,执行命令:TlbImp /out:Interop.Excel.dll Excel.exe。生成Interop.Excel.dll

在项目中去除了Microsoft.Office.Interop.Excel.dll的引用,添加Interop.Excel.dll,将顶部的using Excel=Microsoft.Office.Interop.Excel,该为using Excel= Interop.Excel,本地运行程序没有问题。再次更新服务器端程序,结果又出了新的异常:检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件时失败,原因是出现以下错误: 80080005

五. "80080005异常"与权限配置

"80080005异常"是应用程序对COM组件操作权限不足引起的。于是有了下面的操作:

1)控制面板->管理工具->组件服务->计算机->我的电脑->DCOM-> Microsoft Excel 应用程序

2)单击属性打开此应用程序的属性对话框。

3) 单击标识选项卡,然后选择交互式用户。

4) 单击默认安全性选项卡。设置当前服务器与ASP.NET相关的用户的访问权限。

5)单击启动权限的编辑默认值。设置ASP.NET相关的用户的访问权限。

至此,程序才运行成功,可谓一波三折。

六.关闭Excel进程

如果使用这种方式用户每导出一个Excel文件,服务器端就会启动一个Excel进程。这回引起两个问题,一是耗费服务器资源,二是当进程数达到上限时,会引发异常,调用COM失败。这个时候要想办法结束Excel进程。我采用了杀进程的方式,类似下面的做法:

private void DoExcel()
{
Microsoft.Office.Interop.Excel.Application application = new Microsoft.Office.Interop.Excel.Application();
//这里释放所有引用的资源
application.Quit();
KillExcel(application);
}

[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd,out int ID);
public static void KillExcel(Microsoft.Office.Interop.Excel.Application excel)
{
IntPtr t = new IntPtr(excel.Hwnd); //得到这个句柄,具体作用是得到这块内存入口

int k = 0;
GetWindowThreadProcessId(t, out k); //得到本进程唯一标志k
System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById(k); //得到对进程k的引用
p.Kill(); //关闭进程k
}

 

 

七.还有其他选择

生成Excel方式不止一种,比如利用OpenXml,还是留给你们导论吧,我向你们学习。

 


转载请注明本文地址: 一次艰难的ASP.NET生成Excel文件的历程总结
  • 0
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

技术基础 New Folder 多样式星期名字转换 [Design, C#] .NET关于string转换的一个小Bug Regular Expressions 完整的在.net后台执行javascript脚本集合 ASP.NET 中的正则表达式 常用的匹配正则表达式和实例 经典正则表达式 delegate vs. event 我是谁?[C#] 表达式计算引擎 正式发布表达式计算引擎WfcExp V0.9(附源码) 运算表达式类的原理及其实现 #实现的18位身份证格式验证算法 身份证15To18 的算法(C#) 一组 正则表达式 静态构造函数 忽略大小写Replace效率瓶颈IndexOf 随机排列算法 理解C#中的委托[翻译] 利用委托机制处理.NET中的异常 与正则表达式相关的几个小工具 你真的了解.NET中的String吗? .NET中的方法及其调用(一) 如何判断ArrayList,Hashtable,SortedList 这类对象是否相等 帮助解决网页和JS文件中的中文编码问题的小工具 慎用const关键字 装箱,拆箱以及反射 动态调用对象的属性和方法——性能和灵活性兼备的方法 消除由try/catch语句带来的warning 微软的应试题完整版(附答案) 一个时间转换的问题,顺便谈谈搜索技巧 .net中的正则表达式使用高级技巧 (一) C#静态成员和方法的学习小结 C#中结构与类的区别 C#中 const 和 readonly 的区别 利用自定义属性,定义枚举值的详细文本 Web标准和ASP.NET - 第一部分 XHTML介绍 在ASP.NET页面中推荐使用覆写(Override)而不是事件处理(Event Handler) 常用编码工具类,支持base64,md5,des,crc32 也谈谈技术面试 在C#里把ArrayList转换为Array 或 把Array转换为ArrayList C# 2.0 在.NET 2.0中,让你的组件也可以绑定 .NET20 一种简单的窗口控件UI状态控制方法 翻译MSDN文章 —— 泛型FAQ:最佳实践 Visual C# 3.0 新特性概览 C# 2.0会给我们带来什么 泛型技巧系列:如何提供类型参数之间的转换 C#2.0 - Object Pool 简单实现 Attributes in C# 手痒痒,也来个c# 2.0 object pool 泛型技巧系列:用泛型打造可复用的抽象工厂 体验.net2.0的优雅(四):Provider、策略、控制反转和依赖注入 泛型最佳实践 asp.net 2.0下嵌套masterpage页的可视化编辑 C# 2.0与泛型 动态调用对象的属性和方法——性能和灵活性兼备的方法 泛型技巧系列:用泛型打造可复用的抽象工厂 泛型技巧系列:如何提供类型参数之间的转换 .NET 2.0 泛型Quiz Visual Studio 2005体验泛型编程 C++ 泛型编程系列讲座之实施 泛型技巧系列:简单类型选择器 C# 泛型简介 我眼中的C#2.0新功能特性 泛型技巧系列:避免基类及接口约束 New Article 不该用Generics实现Abstract Factory的理由 C#2.0-泛型 C#2.0-extern C#2.0-可空类型 C#2.0-分部类 C#2.0-迭代器 C#2.0 的新增功能学习 泛型的序列化问题 .NET 2.0 泛型在实际开发中的一次小应用 C#2.0 Singleton 的实现 .Net Framwork 强类型设计实践 通过反射调用類的方法,屬性,字段,索引器(2種方法) ASP.NET: State Server Gems 完整的动态加载/卸载程序集的解决方案 从NUnit中理解.NET自定义属性的应用(转载) 如何在.NET中实现脚本引擎 (CodeDom篇) .NET的插件机制的简单实现 我对J2EE和.NET的一点理解 难分难舍的DSO(一) InternalsVisibleToAttribute,友元程序集访问属性 Essential .NET 读书笔记 [第一部分] NET FrameWork的Collections支持 .NET的反射在软件设计上的应用 关于跨程序集的反射 实现C#和VB.net之间的相互转换 深入剖析ASP.NET组件设计]一书第三章关于ASP.NET运行原理讲述的补白 asp.net 运行机制初探(httpModule加载) 利用反射来查看对象中的私有变量 关于反射中创建类型实例的两种方法 ASP.Net应用程序的多进程模型 NET委托:一个C#睡前故事 [推荐] - [原创] Microsoft .NET策略及框架概述 卸载Class? Web Form 窗体 如何实现web页面的提
©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值