资源描述
在.NET 2.0下用interop组件开发报表及IIS6.0下发布权限解析
赵士坤 刘为 蒲波
西部钻探工程有限公司克拉玛依钻井公司 克拉玛依市 834009
摘要 .NET2.0技术是当下流行的WEB开发技术,事非经过不知难,这句话用在程序开发领域再实际不过了,和大多数从事.NET下WEB开发程序员一样,本人也走过从JSP+SERVLET转到C/S开发,再转到.NET下WEB开发的曲折历程,深知在JSP里面做点事要费多少工夫,找多少开源的报表组件才能成行,现在.NET下好多控件、组件都被标准化了,大大的提高了工作效率,减少了大量的无意义的重复劳动。本文讲述了在.NET2.0开发平台下如何利用COM中提供的INTEROP组件生成定制的EXCEL报表的方法,以及在IIS6.0服务器发布后出现异常、错误的解决方案,类似的实现方案很多,本文是在本人开发实践中遇到的比较典型的报表问题的解决方案,比较而言目前这种方案是比较优秀的,希望通过和大家的分享能给正在从事.NET 平台下WEB开发的初学者一点启示和帮助。
关键词 VBA IIS6.0 权限
1 背景介绍
报表功能是大部分管理系统的基本功能。本人在开发新疆油田开发公司的开发钻井结算管理系统过程中对统计报表的解决方案积累了一定的经验,报表的形式有很多种方案,如EASTREPORT,E报表,水晶报表等等,比较有代表性的是水晶报表,也有很多人用EXCEL做报表,在这个系统中本人使用的就是VBA编程的方式生成EXCEL报表,然后提供下载和在线打开报表功能,EXCEL报表的优势是通用性强,目前大多数人都使用OFFICE的办公软件,EXCEL的使用率非常的高,大家也比较熟悉,生成EXCEL报表后使用者可以根据需要可以进行个性化的数据汇总、分类、总结,比较适合于数据管理系统软件的报表功能,给使用者也提供了便利,下面将对如何在.NET 2.0平台下生成EXCEL报表,以及使用模板方式、非模板方式的优缺点、发布到服务器后需要在IIS服务器端进行的配置进行分析,系统对从事使用.NET平台进行数据库软件开发的开发人员提供一点借鉴。
2 程序架构分析
2.1 系统分析
在一般的企业应用开发中都会涉及到报表的生成,且一般报表的格式都是生成Excel格式的。对于各种报表的生成一直是程序员心中永远的痛,因为对于很多程序员来说每写一个报表都意味着要写一大断代码来实现,而且有的报表可能极其复杂以及不规范,这时就会浪费程序员大量的时间来编写和调试这些代码,那到底有没有一种方法能使代码尽量少写,且又能实现各种Excel报表的生成呢,下面我们就转入正题。 我们要生成Excel报表,其实我们只需要两样东西:
1、报表的样式,也就是哪个字段应该填在哪里,哪一列应该填什么,字体大小,颜色,单元格高度,宽度,单元格是否合并,是否跨行,是否跨列,是横向报表(所谓横向报表,我自定义为:一张Excel报表就是有一条记录或者有一条记录以及其关联记录而生成的报表,如一张订单报表通常就是有一个订单头和其相关的订单明细所生成,这个就是我自定义的横向报表)还是纵向报表(所谓纵向报表,我自定义为:一张报表就是有一个记录集所生成的,如要生成一张记录某个班级学生情况的报表,则要把这个班级的学生记录都要导出到这张报表中,这种报表基本上就是统计报表,一张报表可能会有几万条记录或者更多,这个就是我自定义的纵向报表)等等报表样式。
2、报表的数据,对于我们而言有了报表样式,我们就知道在哪个或者哪几个单元格应该填哪些数据,具体的数据,我们则可以以DataTable的形式来表现,也可以以DataView的形式来表现,至于用怎么样的数据格式可以随自己喜欢,自己来定义。
现在我们知道了生成Excel报表只要有了样式和数据,就可以生成一张自己想要的报表了,数据的生成并不困难,但是我们的困难就是这个报表的样式用什么来描述,怎么来描述。
原理是利用.NET 提供的COM组件中的EXCEL11组件操作EXCEL,有很多种方式,可以直接打开模板,向其中需要的单元格中写入数据,也可以用NEW 的方式在内存中创建一个临时的EXCEL实例对象,想其中写入数据,无论用哪种方式基本原理都是调用EXCEL11组件的interop.dll动态链接库中的EXCEL类定义EXCEL实例,向其中写入数据。下面就具体的实现过程加以介绍。
2.2 环境配置及实现
引用组件Microsoft Excel 11.0 Object Library
2-1 添加引用microsoft excel 11.0 object library
引用组件成功后将在程序目录BIN下生成Interop.Excel.dll动态链接库如下:
2-2 添加引用后BIN目录下生成的动态库
这样就可以开始开发生成EXCEL报表的程序了,首先要在类文件中语句引用类:
using Excel; //引用EXCEL类
using System.Reflection; //引用这个才能使用Missing字段
程序操作EXCEL生成报表的应用有两种实现方式,一种是读模板的方式,另一种是不使用模板的方式。其实读不读模板都能达到一样的效果,看实际情况而用了。
读模板方式:
#region 使用模板生成Excel表
string save_path = "", tick = "";
ExcelOperate excelOperate = new ExcelOperate();
string temp_path = Server.MapPath("../ExportReports");//生成的文件存放路径
string template_path = Server.MapPath("../drilling");//模板路径
string url = ConfigurationManager.AppSettings["path"].ToString();
if (!Directory.Exists(temp_path)) //判断存放模板副本EXCEL文件的路径是否存在,如果不存在则创建一个目录芳芳文件
{
Directory.CreateDirectory(temp_path);
Directory.CreateDirectory(template_path);
}
try
{
#region 使用模板导出Excel表
//建立一个Excel.Application的新进程
Excel.Application app = new Excel.Application();
if (app == null)
{
return;
}
app.Visible = false;
app.UserControl = true;
Workbooks workbooks = app.Workbooks;
_Workbook workbook = workbooks.Add(template_path + "\\DrillReport1.xls");
Sheets sheets = workbook.Worksheets;
_Worksheet worksheet = (_Worksheet)sheets.get_Item(1); //得到SHEET1
if (worksheet == null)
{
return;
}
//想SHEET1指定单元格中输入需要的信息
worksheet.Cells[3, 2] = Session["qk2"] + "钻井工程";
worksheet.Cells[5, 2] = Session["dw3"];
worksheet.Cells[6, 5] = sum_cbhj + "万元";
worksheet.Cells[7, 2] = input_person;
worksheet.Cells[7, 5] = magager;
worksheet.Cells[15, 1] = Session["pcrq"];
tick = DateTime.Now.Ticks.ToString();
save_path = temp_path + "\\" + tick + "开发钻井工程费用结算书" + ".xls";
url += tick + ".xls";
workbook.SaveAs(save_path, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Excel.XlSaveAsAccessMode.xlNoChange, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value); //存储副本到指定的路径,MISSING.VALUE是SAVEAS参数具体含义请参考MSDN技术手册
excelOperate.Dispose(worksheet, workbook, app);//关闭Excel进程
#endregion
//Response.Write(save_path);
Response.Redirect("XLS_DownLoad.aspx?path=" + save_path); //将存储EXCEL副本的文件路径传递给XLS_DownLoad.aspx页面
//Response.Write(url);
}
catch
{
Response.Write("Error");
}
finally
{
Response.End();
}
#endregion
至此,模板方式生成EXCEL报表完成,接下来就是下载该文件XLS_DownLoad.aspx页面的后台代码,是经典的利用浏览器回传下载:
if (path != null && File.Exists(path))
{
System.IO.FileInfo file = new System.IO.FileInfo(path);
//清除缓冲区流中的所有内容输出
Response.Clear();
//将下载保存对话框指定默认的文件名添加到HTTP头中
//Response.AddHeader("Content-Disposition", "attachment; filename=" + file.Name);
Response.AddHeader("Content-Disposition", "attachment; filename=" + System.Web.HttpUtility.UrlEncode(file.Name, System.Text.Encoding.UTF8));//避免中文出现乱码现象
//在header中指定文件的大小,使浏览器能显示下载过程
Response.AddHeader("Content-Length", file.Length.ToString());
//设置输出流的 HTTP MIME 类型
Response.ContentType = "application/octet-stream";
// 发送文件流到客户端
Response.WriteFile(file.FullName);
// 停止该页的执行
Response.End();
}
else
{
Response.Write("文件自动下载中断,请手动下载");
}
非模板方式:非模板方式几乎和模板方式先动只是出去读模板过程
string template_path = Server.MapPath("../drilling");//模板路径
_Workbook workbook = workbooks.Add(template_path + "\\DrillReport1.xls");
直接实例化一个WORKBOOK
_Workbook workbook =new workbooks(temp_path);然后直接向EXCEL文件中写入数据,保存到指定的文件夹路径中,将路径传递给XLS_DownLoad.aspx页面下载。这样EXCEL报表就生成下载完毕,对于比较复杂的中国式报表等,就要根据需要利用VBA编程的技巧控制、合并、设置背景等等的操作完成报表绘制工作。
3 系统发布异常处理
3.1 COM组件的安全性设置
当把编译好的程序发布到IIS6.0服务器上后,如果没有进行DCOM设置会报错:
检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件时失败,原因是出现以下错误: 80070005。
说明: 执行当前 Web 请求期间,出现未处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。 报错的原因是DCOM组件要进行安全性设置,默认情况下是关闭的,必须要对MICROSOFT EXCEL 服务的属性进行设置如下:在系统-运行中输入DCOMCNFG回车,进入到组件服务设置窗口按下图
3-1 组件服务树
3-2MICROSOFT EXCEL应用程序属性设置
3-3 安全性设置
3-4 标识设置
具体的设置步骤及原理如下所述:
异常详细信息: System.UnauthorizedAccessException: 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件时失败,原因是出现以下错误: 80070005。
以启动用户的身份配置 Office
要以启动用户的身份设置 Office 自动化服务器,请按照下列步骤操作:
启动要自动运行的 Office 应用程序。这会强制该应用程序进行自我注册。
运行该应用程序后,请按 Alt+F11 以加载 Microsoft Visual Basic for Applications (VBA) 编辑器。这会强制 VBA 进行初始化。
关闭应用程序,包括 VBA。
单击“开始”,单击“运行”,然后键入 DCOMCNFG。依次展开“组件服务”、“计算机”、“我的电脑”和“DCOM 配置”,然后单击要自动运行的应用程序。下面列出了应用程序的名称:
Microsoft Access 97 - Microsoft Access 数据库
Microsoft Access 2000/2002/2003 - Microsoft Access 应用程序
Microsoft Excel 97/2000/2002/2003 - Microsoft Excel 应用程序
Microsoft Word 97 - Microsoft Word Basic
Microsoft Word 2000/2002/2003 - Microsoft Word 文档
单击“属性”打开此应用程序的属性对话框。
单击“安全”选项卡。验证是否已选中“使用默认的访问权限”和“使用默认的启动权限”。
单击“标识”选项卡。验证是否已选中“启动用户”。
单击“确定”,关闭属性对话框并返回到主应用程序列表对话框。
在“DCOM 配置”对话框中,单击“默认安全性”选项卡。
单击访问权限的“编辑默认值”。验证访问权限中是否列出了下列用户,如果没有列出,则添加这些用户:
SYSTEM
INTERACTIVE
Everyone
Administrators
OfficeAutomationUser
IUSR_<machinename>
IWAM_<machinename>
这些帐户仅在计算机上安装有 Internet Information Server (IIS) 的情况下才存在。
确保允许每个用户访问,然后单击“确定”。
单击启动权限的“自定义”,”编辑”。验证启动权限中是否列出了下列用户,如果没有列出,则添加这些用户:
SYSTEM
INTERACTIVE
Everyone
Administrators
OfficeAutomationUser
IUSR_<machinename>
IWAM_<machinename>
这些帐户仅在计算机上安装有 IIS 的情况下才存在。
确保允许每个用户访问,然后单击“确定”。
单击“确定”关闭 DCOMCNFG。
4 总结
基于.NET2.0平台的报表方式很多,有直接利用成熟的报表工具例如:水晶报表等工具软件,也有利用OFFICE自带的组件生成EXCEL、WORD等格式的报表,这种应用形式有越来越受欢迎的势头,这其中的原因不用多说大家也能理解,就是因为用户的欢迎,任何一个好的系统,用户是推动软件发展方向的外在动力,OFFEICE软件以其使用的广泛性,通用性已经成为了事实上的办公软件标准,.NET平台依托着微软的强大技术支持,在生成报表时提供了诸多的组件支持,使得报表功能越来越强,越来越方便,用户的需求决定了程序员采用哪种报表形式,但随着时间的发展,现在看来WEB开发中使用OFFICE报表已经成为一种趋势,这就要求我们做WEB程序开发的程序员至少要掌握一种可以通用的行之有效的方案,来解决这个重要的报表需求,上文所介绍的COM组件中的EXCE11组件只是操作EXCEL文件生成报表的方式之一,还有很多类似的方法,使用不同的组件,这就要求我们不断尝试,在事件中积累经验掌握技术发展的前沿信息,不断的探索尝试,为以后的开发提供良好的发展基础,开发出使用户满意的程序。
参考文献
【1】 刘端阳,汪杰 ASP.NET 网络开发实用工程案例 人民邮电出版社 2008.5第一版
【2】 郝刚 ,ASP.NET 2.0 开发指南 2006.5第一版
【3】 DR.Shahram khosravi ASP.NET 2.0服务器控件与组件开发高级编程 2007.6第一版
作者简介
刘 为 40岁,1989年参加工作,现工作单位是克拉玛依钻井公司信息服务中心,担任克拉玛依钻井公司信息中心主任,从事过软件系统开发、网络设计、集成电路设计,熟悉汇编语言、PB、标准C、.NET C#等开发工具,参与了《钻井井史管理系统》、《HSE仿真培训系统》等大型管理系统开发以及主持《钻井公司网络改造方案设计》等网络改造工作。
赵士坤 32岁,2003年7月参加工作,工作单位是克拉玛依钻井公司信息服务中心,现担任克拉玛依钻井公司信息中心数据管理部主任,主要从事数据库管理系统软件研发工作,熟悉JSP+SERVLET,PB,.NET2.0等开发工具,参与开发《固井工程设计与数据分析系统》、《钻头管理系统》、《HSE证件管理系统》等管理软件,目前都在投入使用中,正在开发的《开发钻井结算管理系统》目前钻井结算部分已经开发完成。
蒲 波 28岁,2004年参加工作,就职于克拉玛依钻井公司信息服务中心,从事软件开发及网络维护工作,熟悉PB,C#等开发工具,参与了《钻头管理系统》、《HSE证件管理系统》、《钻井结算管理系统》开发。附录资料:
Ehcache缓存配置
简介
Cache的配置很灵活,官方提供的Cache配置方式有好几种。你可以通过声明配置、在xml中配置、在程序里配置或者调用构造方法时传入不同的参数。
你可以将Cache的配置从代码中剥离出来,也可以在使用运行时配置,所谓的运行时配置无非也就是在代码中配置。以下是运行时配置的好处:
· 在同一个地方配置所有的Cache,这样很容易管理Cache的内存和磁盘消耗。
· 发布时可更改Cache配置。
· 可再安装阶段就检查出配置错误信息,而避免了运行时错误。
本文将会对ehcache.xml配置文件进行详细的阐述。在配置的时可以拷贝一个现有的ehcache.xml,如果没有请点击这里去下载。
ehcache-failsafe.xml
如果你调用了CacheManager默认构造方法去创建CacheManager的实例,此方法会到classpath中找ehcache.xml文件,否则它会到类路径下找ehcache-failsafe.xml文件。而ehcache-failsafe.xml被包含在jar包中,所有它肯定能找的到。
ehcache-failsafe.xml提供了一个非常简单的默认配置,这样可以使用户在没有创建ehcache.xml的情况下使用Ehcache。
不过这样做Ehcache会提醒用户创建一个正确的Ehcache配置。
ehcache.xml片段:
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>
ehcache.xml和其他配置文件
在Ehcache-1.6之前的版本,只支持ASCII编码的ehcache.xml配置文件。在Ehcach-1.6之后版本中,支持UTF8编码的ehcache.xml配置文件。因为向后兼容,所有采用ASCII编码的配置文件完全没有必要转换为UTF8。
一个CacheManager必须要有一个XML配置。由于磁盘路径或是监听端口,多个CacheManager使用同一个配置文件时会出现错误。
下面是ehcache.xml具体实例以及配置指南
<ehcache xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
· CacheManager配置
DmulticastGroupPort=4446,这样可以配置监听端口。
· DiskStore配置
如果你使用的DiskStore(磁盘缓存),你必须要配置DiskStore配置项。如果不配置,Ehcache将会使用java.io.tmpdir。
diskStroe的“path”属性是用来配置磁盘缓存使用的物理路径的,Ehcache磁盘缓存使用的文件后缀名是.data和.index。
<disStore path=”java.io.tmpdir”/>
· CacheManagerEventListener配置
我们通过CacheManagerEventListenerFactory可以实例化一个CacheManagerPeerProvider,当我们从CacheManager中added和removed Cache时,将通知CacheManagerPeerProvider,这样一来,我们就可以很方面的对CacheManager中的Cache做一些统计。
注册到CacheManager的事件监听类名有: adding a Cache和removing a Cache
<cacheManagerEventListenerFacotory class=”” properties=””/>
· CacheManagerPeerProvider配置
在集群中CacheManager配置CacheManagerPeerProviderFactory创建CacheManagerPeerProvider。具体的实例如下:
<cacheManagerPeerProviderFactoryclass="net.sf.ehcache.distribution.
RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual, rmiUrls=//server1:40000/sampleCache1|//server2:40000/sampleCache1| //server1:40000/sampleCache2|//server2:40000/sampleCache2"
propertySeparator="," />
· CacheManagerPeerListener配置
CacheManagerPeerListener配置是用来监听集群中缓存消息的分发的。
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=fully_qualified_hostname_or_ip,
port=40001,
socketTimeoutMillis=120000"
propertySeparator="," />
· Cache配置
· name:Cache的唯一标识
· maxElementsInMemory:内存中最大缓存对象数。
· maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大。
· eternal:Element是否永久有效,一但设置了,timeout将不起作用。
· overflowToDisk:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中。
· timeToIdleSeconds:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
· timeToLiveSeconds:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,也就是element存活时间无穷大。
· diskPersistent:是否缓存虚拟机重启期数据。(这个虚拟机是指什么虚拟机一直没看明白是什么,有高人还希望能指点一二)。
· diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
· diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
· memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。这里比较遗憾,Ehcache并没有提供一个用户定制策略的接口,仅仅支持三种指定策略,感觉做的不够理想。
· Cache Exception Handling配置
<cacheExceptionHandlerFactory class="com.example.ExampleExceptionHandlerFactory" properties="logLevel=FINE"/>
总结
这里只对通用缓存的配置做了详细的阐述,至于RMI缓存和集群缓存可以参考这里。
下面给出几个配置示例:
· Ehcache默认Cache配置
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
· SampleCache1配置
简单配置,在ehcache.xml文件中有此配置,在使用Ehcache前最好将其删除掉,自己配置。
缓存名sampleCache1,内存中最多可缓存10000个Element,其中的element会在闲置5分钟或是存活10分钟之后失效。
超过10000element时,element将会输出到磁盘中,输出路径是java.io.tmpdir。
<cache name="sampleCache1"
maxElementsInMemory="10000"
maxElementsOnDisk="1000"
eternal="false"
overflowToDisk="true"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
/>
· SampleCache2配置
Cache名为SampleCache2,内存中最多可以缓存1000个element,超出1000不能输出到磁盘中。缓存是永久有效的。
<cache name="sampleCache2"
maxElementsInMemory="1000"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="FIFO"
/>
· SampleCache3配置
Cache名为SampleCache3。可缓存到磁盘。磁盘缓存将会缓存虚拟机重启期的数据。磁盘缓存失效线程运行间隔时间是10分钟。
<cache name="sampleCache3"
maxElementsInMemory="500"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="1"
memoryStoreEvictionPolicy="LFU"
/>
· sampleDistributedCache1配置
Cache名为sampleDistributedCache1。
<cache name="sampleDistributedCache1"
maxElementsInMemory="10"
eternal="false"
timeToIdleSeconds="100"
timeToLiveSeconds="100"
overflowToDisk="false">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
</cache>
·
展开阅读全文