准备做一个基于jasperreport 的报表打印,之前实现了一版,调用的是服务器端的打印机,但是不太符合要求,准备改成applet方式,实现客户端本地打印。
我们的客户端软件用的是eclipse的插件开发。
所用的applet,使用的是jasperreport3.0的例子中的webapp。用ant编译后导出一个war包,然后放到tomcat下面即可运行。
我在运行例子的时候,我的ie不能显示,看了源码,例子中使用的是object标签,我把object标签删掉,用下面注掉的applet标签,可以正常使用。
下面把例子放到我自己的工程中,出现问题,在运行例子中的Servlet的时候,总是提不正常。先开始是提示jasperreport中使用的common-loggin和我工程中使用的log4j冲突,common-logging不能获取实例。在经历了换版本,jar包换位置(就是都放到tomcat的bin目录下去,删去工程目录中的。)等等,终于不报common-loggin的错了,又开始提示找不到jasperreport中的类,那个类明明在那里,而且是jasperreport运行到中间的时候的一个类,什么什么编译用的,具体名字忘了,所以肯定不是jasperreports-3.0.0-applet.jar的位置的问题。这个问题困扰我2天,然后有次无意中把Servlet换了个包。。好了。靠。。至今也不明白为什么。
下面给出部分主要代码:
applet代码就不贴了,基本上和例子中的一样,就是删去了一个按钮,并且去掉了打印机选择的那个对话框
servlet我没有用例子中的,而是改成了调用一个action,其实这里写什么都是一样的,因为在applet中主要是调用了jasperPrint = (JasperPrint)JRLoader.loadObject(url);这一句,这个url需要是一个装填好的jasperPrint,如果之前生成了这样的文件,那么哪怕直接写文件的地址都可以。
action中的主要代码是这样的。
PrintFromAppletAction.java
- @Override
- public ActionForward ExecuteIt(FrameActionMapping mapping, FrameForm form, HttpServletRequest request, HttpServletResponse response, IActExcResult actExcResult)
- throws IOException, ServletException {
- String cardid = request.getParameter("key_CARDID");
- String showbackground="1";
- // 构造传入报表的参数
- HashMap param = new HashMap();
- param.put("showbackground", showbackground);
- // 获取数据库连接
- Connection conn = null;
- try{
- conn = DBConnectionMgr.getConnection(FrameConstant.MID_DATASOURCE_LEVEL);
- //构造查询sql
- String sql="//一些sql,占地方,不写了";
- //执行查询
- DaoQryHelper objDaoQryHelper = DaoQryHelper.getInstance();
- ArrayList spotList = objDaoQryHelper.getQueryStringList(conn,sql, null);
- if (spotList != null && spotList.size() > 0) {
- spotList = (ArrayList) spotList.get(0);
- if (spotList.size() > 0) {
- //将sql的查询结果put进param
- } else {
- System.err.println("查询返回空:spotList.get(0)");
- }
- } else {
- System.err.println("查询返回空:spotList");
- }
- } catch (Throwable e) {
- e.printStackTrace();
- request.getSession(true).setAttribute(Globals.ERROR_KEY, "查询数据库错误:<br>" + e.getMessage());
- return mapping.findForward("error");
- } finally {
- DBConnectionMgr.freeConn(conn);
- }
- String jasperFileName="prescription.jasper";
- // 获取.jasper文件路径
- EnsureFolderExist(getSysRootPath()+"reportCustomization");
- String jasperFilePath = getSysRootPath()+"reportCustomization//"+jasperFileName;
- JasperPrint jasperPrint = null;
- try {
- jasperPrint=JasperFillManager.fillReport(jasperFilePath, param, new JREmptyDataSource());//这句是关键,这句生成了这个jasperPrint就是要传给applet的。
- } catch (JRException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- if (jasperPrint != null)
- {
- response.setContentType("application/octet-stream");
- ServletOutputStream ouputStream = response.getOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(ouputStream);
- oos.writeObject(jasperPrint);
- oos.flush();
- oos.close();
- ouputStream.flush();
- ouputStream.close();
- }
- else
- {
- System.err.println("jasperPrint为空");
- return null;
- }
- return mapping.findForward("");
- }
下面是页面的调用,是在一个jsp页面。
- <object
- classid = "clsid:CAFEEFAC-0015-0000-0000-ABCDEFFEDCBA"
- codebase = "<%=request.getContextPath()%>/jre-1_5_0_16-windows-i586-p.exe"
- WIDTH = "80" HEIGHT = "35" >
- <PARAM NAME = CODE VALUE = "PrinterApplet.class" >
- <PARAM NAME = CODEBASE VALUE = "applets" >
- <PARAM NAME = ARCHIVE VALUE = "jasperreports-3.0.0-applet.jar" >
- <param name = "type" value = "application/x-java-applet;jpi-version=1.5">
- <param name = "scriptable" value = "false">
- <PARAM NAME = "REPORT_URL" VALUE ="../printFromAppletAction.do?key_CARDID=<%=printID%>">
- <PARAM NAME = "SHOW_DIALOG" VALUE ="true">
- <input type="button" value="服务器端打印" onclick="javascript:printPdf()">
- </object>
这个object标签大部分是转过来了,他的原型是这样的:
- <APPLET CODE = "PrinterApplet.class" JAVA_CODEBASE = "applets" ARCHIVE = "jasperreports-3.0.0-applet.jar" WIDTH = "80" HEIGHT = "35">
- <PARAM NAME = "REPORT_URL" VALUE ="../printFromAppletAction.do?key_CARDID=<%=printID%>">
- <PARAM NAME = "SHOW_DIALOG" VALUE ="true">
在jdk的bin目录中有一个叫做HtmlConverter.exe的工具,在cmd中输入HtmlConverter -gui可以打开图形界面。
选择好目录以后按转换,就可以将applet标签变成object标签。
applet标签是ie以前用的,现在applet的标准很混乱,而且applet标签功能也比较少,所以现在sun推荐大家使用object标签。
object标签的好处是可以检查你有没有jre,没有的话可以给你下一个。
关于object 的codebase和code,要和项目中的路径对应好,否则很容易出现找不到类的问题。
其中,applet虽然是类,但是不要放在eclipse的src目录下面,也不要有任何包。
对于我这个,我的applet放在了工程根目录下面的applets文件夹,applets在codebase中指明。
ARCHIVE 是指运行这个applet需要的一些环境,我加载了jasperreports-3.0.0-applet.jar,这个类可以在jasperreport的例子中找到。
jasperreports-3.0.0-applet.jar也被窝放到了applets文件夹下面。
下面要说自动安装jre的问题。
因为我的应用环境是一个小型局域网,并且部署环境有可能不能上外网,所以,我希望把jre放在局域网中的服务器上。
用htmlconverter转换完以后的object的codebase本应是这样的:codebase="http://java.sun.com/update/1.5.0/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0"
这是sun提供的一个cab包,里面有一个jinstall-1_5_0.inf和一个jinstall.exe。其中jinstall-1_5_0.inf是这样写的:
- ; Version number and signature of INF file.
- ;
- [version]
- signature="$CHICAGO$"
- AdvancedINF=2.0
- ; The order of files in this section defines the download order.
- ; Last in First download.
- [Add.Code]
- jinstall.exe
- jpiexp32jpiexp32.dll=jpiexp32.dll
- npjpi150npjpi150.dll=npjpi150.dll
- [jpiexp32.dll]
- FileVersion=1,5,0,0
- RegisterServer=no
- clsid={8AD9C840-044E-11D1-B3E9-00805F499D93}
- hook=bridgeinstaller
- [npjpi150.dll]
- FileVersion=1,5,0,0
- RegisterServer=no
- clsid={CAFEEFAC-0015-0000-0000-ABCDEFFEDCBA}
- hook=bridgeinstaller
- [jinstall.exe]
- file-win32-x86=thiscab
- FileVersion=1,5,0,0
- ; jinstall.exe will be executed.
- ;
- [bridgeinstaller]
- run=%EXTRACT_DIR%/jinstall.exe /installurl=http://java.sun.com/update/1.5.0/1.5.0-b64.xml
看到没,是用jinstall.exe 根据http://java.sun.com/update/1.5.0/1.5.0-b64.xml这个xml文件去下载的。其中1.5.0-b64.xml这个xml文件中有这样一句:
- <title>Java 2 Runtime Environment, v1.5.0-b64</title>
- <description>This is 1.5.0-b64.</description>
- <url>http://javadl.sun.com/webapps/download/GetFile/1.5.0-b64/windows-i586-jre/jre-1_5_0-windows-i586-iftw.exe</url>
按照里面的地址下载那个exe,发现并不是一个offline版本的,而是一个online版本的。也就是说,需要联网才能下载的。
至此,有2种方法可以实现在局域网内的jre自动安装,一种,是我上面用的,直接在object的codebase中写一个jre的exe文件,这个jre放在工程目录下面。这个jre可以从sun的官网上下载。
第二种,是把上述的cab啊,xml啊都下载下来,部署在工程中,但是,最后的xml中,还是要写完整的jre而不是原版中提供的online版的。
这里有点小问题,如果使用sun提供的方式也就是在线下载jre,下载安装完成之后,页面上直接就可以显示出applet来,但是如果使用我的方式,下载完成后就必须重启ie。
算是一点小的瑕疵吧。。不知道怎么才能做到和sun提供的那个一样。
以下,在ie6和ie7下测试正常,其中ie7有时候需要在高级中把jre应用于applet这个选项选中,并且在加载项中把sun控制台启用(据说默认是停用的)。
如果用遨游,需要在设置的高级中,选择使用其它jre。不然它默认使用的微软的jre。
我使用的环境,tomcat5,jasperreport3.0,ie7/6
关于java plugin 可以看http://java.sun.com/javase/6/webnotes/family-clsid.html。
里面介绍了objcet标签的clsid怎么写。
另外,网上看到很多人提到要数字签名,这个我不太懂,我没用,貌似也能正常使用,用了一下,好像也没什么反应。
怎么签名就不写了,也是用jdk的bin中的工具,(keytool),很简单。
感觉,用applet,真是一个过时的东西。慢的要死。还不好看。