Web开发中的报告输出(rtf、xls)

这几天碰到一个看起来比较难处理的一个任务:把数据库中的数据导出为可以打印或者方便办公软件查看的格式。

这是对文档类型输出中,最近解决的一个方法。我考虑到这里大家主要是使用 PDF 输出和 Doc 格式的输出,但是 PDF 格式和 Doc 格式输出都不是很靠谱。其实简单的方法就是利用 RTF 格式进行中转输出。因为 RTF 格式实际上和 HTML 格式类似,也采用文本格式存储并且用文本标签描述的方式为文本添加显示属性,纯文本的操作能够更清楚地考虑进行的操作,错误恢复也会比二进制格式的 PDF 和 Doc 好点。

由于是一个打印文档,或者带有一定格式的文档,发现大家主要采用模版替代的方案。即先把文档编辑好,然后需要填写的内容用一些特殊的文本进行标记。在输出的时候,先读取文本,然后替代预先设置好的文本标记为要输出的内容即可。

Word 格式的输出

Word 格式输出是一个很不明确的说法,大概指的是可以用 Word (Microsoft Word 以及类似软件)打开的格式吧。这种格式的输出大概是为了打印而准备的。由于HTML天生没过多地考虑页面打印的问题。以前做过利用 CSS 的媒体类型来做的打印功能,大家说总是不那么方便。

直接导出 doc 格式文件是首选的。如果采用的是 Java 或者 .NET 平台,这个是一个好消息,因为很多人已经做过这方面的尝试了。比较好用的库有 Apache POI(For Java)以及对应的 .NET 版本 NPOI (For .NET)。

在 PHP 或者其他脚本语言(例如 Python 等)就没找到比较好用的库了,有谁了解的给我留个言吧。但是,临时的解决方法也不是没有,可以尝试输出 RTF 文件格式。这种文件格式和 HTML 类似,也是用纯文本编码,用标记来为文本添加格式等(只是标记不一样)。因此,只需要编辑好模板 Word 文件,然后保存为 RTF 格式,然后要输出的时候,把这个 RTF 格式的文件直接读入,然后进行字符串替换就好了。

不过,需要注意的是,由于格式化标签的存在,你输出的替代文本可能不是连着的(参考一下 HTML 代码,对于 RTF 来说也有类似的问题,只是标签格式不一样),文本替换的时候也很难发现,导致输出的时候根本替换不了。

例如,你用来做模板替换的内容是 \$PLACEHOLDER\$ 而实际上,在文件中表达却是:







<span><span>$</span><span>PLACEHOLDER</span><span>$</span></span>

这个问题实际上就是无效格式相互嵌套,显示效果还是无效格式的情况。或许可以用离散数学里面的关系给它命名为“幂等性”(这也是常说的,工具生成冗余代码的原因)。所以,在考虑输出的时候一定要先考察模板文件是否存在这个情况。

对于简单的情况,使用 RTF 格式或许更易于排错

XLS 输出

越来越觉得 XLS 是一种重要的格式。虽然不是什么标准,但是,非技术人员都对这种表格形式的格式十分熟悉,而且用户体验也比较好。所以,作为一种数据交流格式,输出 XLS 是一种不错的选择。而业界似乎对 XLS 格式已经做了很多研究了,很多语言都有对应的版本读写库。

在 Java/.NET 平台中,还是采用 Apache POINPOI For .NET)库。这个库的两个版本对 XLS 读写支持都是很完善的,不过对于 .NET 平台还可以采用微软官方的库(可能依赖 Microsoft Office 以及其授权)。

在 PHP 中可以采用 PHPExcel 库,这个库可以输出包括 Excel 97~2003 格式的xls格式,还可以输出类似的 xlsx 格式和 ods 格式。


现在发现,其实这些格式在深入研究以后都不是很困难,即使是非文本格式的,其实也是采用附加格式进行编码的,而困难在于了解其编码的方式。

有更好的解决方案或者更好的库推荐可以留言提出来,帮助更多有需要的人。:)

使用 Maven 管理 Java 项目

 

使用 Maven 并不是什么新鲜事了。不过,如果不是接触到 Node.js 里的 NPM 和 git ,或许我还真的很少会考虑到项目管理的问题。我想或许是以前真的很少接触过很多依赖关系的项目吧,但是,如果开发基于 Spring 的应用程序,依赖缺失的问题真的是一个十分困扰的问题。建议所有学习 Spring 的人都学习一下 Maven 这个工具。

稍微采纳一段来自百度百科的简介:(有修改,详情请参考 百度百科的 Maven 介绍 )

Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具。 Maven这个单词来自于意第绪语,意为知识的积累,最早在 Jakata Turbine 项目中它开始被用来试图简化构建过程。当时有很多项目,它们的Ant build文件仅有细微的差别,而JAR文件都由CVS来维护。于是Maven创始者开始了Maven这个项目,该项目的清晰定义包括,一种很方便的发布项目信息的方式,以及一种在多个项目中共享JAR的方式。

这个项目和 Node.js 中的 npm 以及 ruby 中的 gem 很相似。简单地说,就是一个描述项目以及项目间关系的一个工具。

使用这个工具的两点好处:

  • 不需要手工维护依赖的文件位置。简化项目间依赖的问题。(在初学 Spring 的时候很容易找不到足够的依赖包,而这个文件可以帮助寻找)
  • 在 git 代码仓库中只保存和本项目相关的信息,其他代码可以通过描述由 Maven 自动获取。

在 Eclipse 中并未集成对 Maven 的支持。如果要在 Eclipse 中添加对 Maven 的支持,可以选择安装 Maven 的 Eclipse 的插件(即 m2e 插件,在 Eclipse 帮助菜单下的 Eclipse Marketplace 中可以搜索到)。对于开发基于 Spring 的程序,可以选择使用 STS 或者 MyEclipse 等第三方工具或者插件(这些工具包含了 m2e)。

只需要在新建项目的时候,选择 Maven 工程即可。对于已经建立的工程来说,也可以在项目窗格下右击项目,然后选择 Configure 菜单下的 Convert to Maven Project 即可。

然后, Maven 项目的关键在于使用 pom.xml 来管理项目以及项目依赖。在 pom.xml 中,主要分为几个区域:与当前项目相关的区域、依赖项、Maven 代码库地址、插件信息、编译代码等等。示例如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId><!-- 项目的组ID --></groupId>
    <artifactId><!-- 项目ID --></artifactId>
    <version><!-- 版本信息 --></version>
    <name><!-- 项目名称 --></name>
    <description><!-- 项目描述 --></description>
    <dependencies>
        <!-- 项目依赖项设置 -->
    </dependencies>

    <repositories>
        <!-- 项目依赖项的代码库,即如何获取依赖 -->
    </repositories>

    <build> <!-- 构建规则 -->
        <sourceDirectory>src</sourceDirectory>
        <resources> <!-- 资源位置 -->
            <resource>
                <directory>src</directory>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </resource>
        </resources>
        <plugins> <!-- 插件 -->
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

对于项目的依赖信息。Eclipse的图形化插件已经提供了相当详细的说明,例如,一个典型的依赖项可以写为:(在 <dependencies> 标签内)

<!-- mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.17</version>
</dependency>

而对于特定的代码库地址,可以在 <repositories> 中定义如下:

<repository>
    <id>EclipseLink</id>
    <url>http://download.eclipse.org/rt/eclipselink/maven.repo</url>
</repository>

至于构建目录以及包含文件,可以参考上述 pom.xml 中 <build> 标签内的内容。

一般来说,要找到 Maven 的依赖地址,可以通过在 Google 中搜索。

Enterprise JavaBean笔记:第一印象

这篇文章只是对 EJB 起一个很初步的认识。

以前觉得,既然有了 JSP 和 Servlet ,基本分离了表现和逻辑,再加上 Filter 作为视图层的补充,Listener 为事件监听。已经是一个比较完善的架构了。EJB 要作为那一部分的补充而存在呢?

实际上,EJB 的存在应该是为了补充 Servlet 对拓展性不足而存在的。EJB 的存在,一个基本的修改的是,把两层结构 user -> Servlet ,修改为 user -> Servlet -> EJB。实际上,如果按照原来的设想,Servlet本身就可以作为一个 Stateless Session Bean,但由于 Servlet 实现的历史原因, Servlet 必须继承于 HttpServlet 类,并且带有 HTTP 的特征,作为视图层的 JSP 实际上是一个特殊化的 Servlet 。也就是说, Servlet 本来也是为单一业务(HTTP)而存在的,算是一个适配器。而客户端技术,却不仅仅只是 HTTP 和 HTML,因此,一个只为业务而生的逻辑层应运而生,即 EJB 。

在《Java EE 6 Tutorial》中对 EJB 的介绍:

You should consider using enterprise beans if your application has any of the following requirements.

  • The application must be scalable. To accommodate a growing number of users, you may need to distribute an application’s components acrossmultiplemachines. Not only can the enterprise beans of an application run on differentmachines, but also their locationwill
  • remain transparent to the clients.
  • Transactionsmust ensure data integrity. Enterprise beans support transactions, the mechanisms thatmanage the concurrent access of shared objects.
  • The application will have a variety of clients. With only a few lines of code, remote clients can easily locate enterprise beans. These clients can be thin, various, and numerous.

EJB 的拓展性,主要体现在 EJB 容器的对象缓存上。由于 EJB 的缓存,客户端(包括 Web 应用)可以很快地获取 EJB 对象。而其他特点,数据一致性、支持多客户端,都基于对象池的设计。

EJB 的类型分为 Session bean 和 Message Driven Bean。 Session Bean 中的无状态 Bean 就是缓存的主要对象。关于这方面的资料,可以参考《The Java EE 6 Tutorial》。

Enterprise JavaBean 笔记:JBoss安装与配置

记得刚上大学不久,学 Java 的时候,就曾经听说过 Enterprise JavaBean 了,但一直没有试用过,因为,从这个名字看来,很容易和 JavaBean 混淆。而在 Enterprise JavaBean 介绍中提到的分布式、可伸缩的网络模块的时候,又显然和作为对象数据存取规范的 JavaBean 规范不同。Enterprise JavaBean 更像是一个遵循 JavaBean 规范的网络服务提供者。一直以来用PHP来开发网站,对 Java 这边规范的 MVC 开发模式还是颇为羡慕。而这个学期就开始 EJB 的旅程了~

这个系列博客大概会持续讨论关于 Enterprise JavaBean 的问题,欢迎大家提出意见。 :-)

环境选择:

这里选择 NetBeans + JBoss AS 5.1 作为开发环境。选择 JBoss AS 5.1 是因为课程需要,其实 NetBeans + Glassfish 的组合可能更好。

  1. JBoss AS 5.1:可以在http://sourceforge.net/projects/jboss/files/JBoss/JBoss-5.1.0.GA找到,下载后解压即可。
  2. Netbeans 7.2:可以在http://www.netbeans.org下载NetBeans 7.2 for Java EE(按提示安装即可,其中可以选择安装安装包带上的Glassfish 3.2.2,Java EE 6兼容服务器)。

配置:

NetBeans安装成功后,如果开始时选择了Glassfish,那么已经有一个Java EE 6兼容服务器。不过,这里选择课堂要求的JBoss Application Server 5.1。

  1. 在NetBeans的服务器平台上,点击“工具”->“服务器”。
  2. 在服务器窗口点击“添加服务器”,按提示添加“JBoss Application Server”,选择JBoss的安装根目录即可。
  3. 启动JBoss AS 5.1,如果出现“java.lang.IllegalArgumentException: Wrong arguments. new for target java.lang.reflect.Constructor expected=[java.net.URI] actual=[java.io.File]”的提示,大多是因为注入AttachmentStore类的时候没有指定注入类型导致。
    编辑%JBOSS_HOME%/server/default/conf/bootstrap/profile.xml文件:
    ……
    
    <bean name="AttachmentStore" class="org.jboss.system.server.profileservice.repository.AbstractAttachmentStore">
        <constructor>
            <parameter>
                <inject bean="BootstrapProfileFactory" property="attachmentStoreRoot" />
            </parameter>
        </constructor>
    ……

    修改<parameter>标签为:

    ……
            <parameter class="java.io.File">
    ……

    即可。

测试:

  1. 新建 EJB 模块。新建 Stateless Session Bean(无状态会话Bean),新建向导创建Local(本地)接口的无状态会话 Bean,添加一个 Bussiness Method (在编辑区域内,按 alt + Insert,即弹出生成代码框,选择 NetBeans 的原因一部分就是因为 NetBeans 挺方便的代码生成功能)。
    代码看起来像:
    package hello.greeting;
    
    import javax.ejb.Stateless;
    
    @Stateless
    public class Greeting implements GreetingLocal {
    
        public String greet(String name) {
            return "Hello, " + name + "!";
        }
    }

    接口如下:

    package hello.greeting;
    
    import javax.ejb.Local;
    
    @Local
    public interface GreetingLocal {
    
        public String greet(String name);
    }
  2. 新建 Web 应用程序项目,编辑项目属性,添加 上面创建的 EJB 模块到 Libraries (库)中,不需要包括在编译包中。这里只是为了让这个 Web 应用程序知道会话 Bean 的接口而已。
    编辑 index.jsp,在空白处添加对 EJB 的引用:
    <%@page contentType="text/html" pageEncoding="UTF-8"%>
    <%@page import="javax.naming.*" %>
    <%
    InitialContext context = new InitialContext();
    GreetingLocal bean = (GreetingLocal) context.lookup("Greeting/local");
    out.println(bean.greet("Jack"));
    %>
  3. 发布 EJB 模块和 Web 应用程序来测试吧~

又到开学时

这周估计很多学校都开学了,虽然东北这边已经开学两周了。

这两周以来其实也没重要的事情。分析了目前的市场形势后,我们团队还是决定放弃原来的Rtank计划。学长也紧张地找工作了。而我打算把以前所做的工作在这里开放。估计会在9月20日左右开放源码。提供一个简易的网络开发模块。暂定名为RNetBase。这个模块的设计之初是为了支持远程控制玩具坦克的。也是我第一次尝试设计,这次也使我深深地感受到,不是各个模块设计好,连接就一定顺利的。现实世界还是被自然控制得很好,很多物件都很灵活,而虚拟世界里面,需要你自己去控制。

2012-8-23

昨天又回到沈阳了,准备新学期的学习。这个学期要学的内容真的很多,包括大学里学分最多的课都在这个学期了。三门重点课程更是我所希望了解的。不过,暂时对此还没有很多计划。不过可以肯定,必须更勤奋了。

在上个学期末,给这个假期定的计划是放松。不过,实际上真的放松不了。回家前定的两个项目,一个为视点网写的Android客户端以及学院希望我们提交的WindowsPhone程序。前一个错误地估计了HTML 5的普及,现在得用Android原生界面重写。大概在新学期的时候可以完成。

在完成Android的程序的时候,也发现了不少有趣的内容,例如,超越所有构造函数的匿名构造函数:

class TestClass {
	private int a;
	private Integer b;
	private long c;

	{
		a = 1;
		b = new Integer(a);
	}

	public TestClass(long c) {
		this.c = c;
	}
}

中间用 { ... } 括起来的就是一个匿名的构造函数,独立于所有构造函数,无论调用哪个构造函数,都会先调用这个构造函数。挺有用的吧。对于静态变量,可以用 static { ... } 来初始化。

另一些发现则是和Android的Looper、Handler、Message类框架有关的类。Looper类的构造和Thread类密切相关,而且Thread类的静态方法Thread.currentThread()可以唯一确定一个Thread然后制作一个线程池进行管理。Looper类的消息机制也建立与此。而且,利用Handler类的消息延迟发送功能,可以巧妙地构建动画,而且这个动画还在当前UI线程,可以安全地更新UI。

 

Java NIO笔记 Channel

记得第一次做计算机网络的实验课的时候,我用多线程实现了一个HTTP服务器,然后对能够同时对应多个客户端的读请求十分满意。没想过线程的开销问题。不过,后来在手机端设计一个需要同步读写的服务器的时候,才突然发现,要实现维护多个线程的同时,同步读写并不是那么容易的事情。

文中有什么问题,希望指出完善。:-)

TankII 一点小更新

发现又很久没更新博客了。

用Box2D重写了TankII,然后绘制则用了AWT和类似于Page flipping的技术,在Windows上绘制看起来是挺流畅的。至于可能有点慢,并不是画面跟不上,而是我设的速度限制而已。这次的版本,虽然地图做的还是很次,但代码管理上应该有不少进步了。眼看就开学了,不知道还有多少时间会像现在这样慢慢地开发程序。不过,有期待的就会有收获。

快速按坐标分类的方法还是没找到,不过现在的绘制速度还可以,于是也没找别的方法来提高绘制速度了。有空还是得转向OpenGL,现在用的Java2D虽然也是跨平台,但是不一定得到很好的硬件加速。而且Android的开发似乎也会是OpenGL的。用OpenGL对于平台切换也简单点吧。

下载:http://www.box.com/s/17djfttkvrovpn05ognv 源码应该会在不久后也上传的,现在没整理好,就没上传了;大概也不是很多人会看……

今年回来,还是有点无奈。不过走到现在,其实有很多人帮助过我了,要知道感恩生活,才知道快乐。

Smile

上学期末的时候写的一个小程序,用于练习 Java2D 编程。我觉得图形编程还是挺有趣的。

记得学期末的时候,某人发短信过来说对编程还是没很多兴趣。嘿嘿,其实我觉得其实编程就不过是实现生活中的事情而已。于是写了这个小程序。希望见到这个小程序也会开心一下吧。

也应该说一下。版权不在我这里,我只是用 Java 参考 Xorg 中的 Xeye 示例程序、加入一些鼠标事件处理写的。

大家可以参考一下源码吧。虽然现在看回原来写的代码,我也觉得写得不是很好(思路不清晰,但代码不长)……需要注意的是,我使用了 JRE 7 的新特性,所以,必须在 JDK 7 下编译。
实际上是因为加入了这些语句:

frame.setShape(new  
	java.awt.geom.Ellipse2D.Double(
		0, 0, 
		frame.getWidth(), 
		frame.getHeight()));
try {
	frame.setOpacity(0.8f);
} catch(Exception ex) {
}

其实还有一些动态效果的。截图就不多发了。

嗯,其实我希望大家见到这个程序都会开心。

下载:http://www.boxcn.net/shared/il58j4fm96mkj6nj0s69

源码:http://www.boxcn.net/shared/umi549k2vjsir2lzc25a

TankII update 1.2.1

考虑到以前同学说过的按键问题,现在修正了一下。不过,要更准确地控制坦克的炮筒位置,就需要两副方向键。而现在 ASDW 和 方向键 (功能和ASDW一样)都用了,于是想到 VI 的键盘模式 HJKL 。

修改记录:

  • 更改键设定,H、J、K、L 控制坦克炮筒角度。

所以,现在是,举例来说,无论现在炮筒在什么方向,只要按了 H 那么就会沿最短的路径向左( J 对应“向下”、K 对应“向上”、L 对应“向右”)。

这个修改太小了。所以就只是更新一个小版本号吧。基本上就修改了 TankKeyListener 类。

下载地址:http://www.boxcn.net/shared/1v7lhfdfumj08sxoktsl (其实下载地址没怎么变,用了 box.net 的文件版本功能)