十二、生成缩略图
当用户上传了图片后,必须生成缩略图以便用户能快速浏览。我们不需借助第三方软件,jdk标准库就包含了图像处理的api。我们把一张图片按比例缩放到120x120大小,以下是关键代码:
public static void createpreviewimage(string srcfile, string destfile) {
try {
file fi = new file(srcfile); // src
file fo = new file(destfile); // dest
bufferedimage bis = imageio.read(fi);
int w = bis.getwidth();
int h = bis.getheight();
double scale = (double)w/h;
int nw = image_size; // final int image_size = 120;
int nh = (nw * h) / w;
if( nh>image_size ) {
nh = image_size;
nw = (nh * w) / h;
}
double sx = (double)nw / w;
double sy = (double)nh / h;
transform.settoscale(sx,sy);
affinetransformop ato = new affinetransformop(transform, null);
bufferedimage bid = new bufferedimage(nw, nh, bufferedimage.type_3byte_bgr);
ato.filter(bis,bid);
imageio.write(bid, "jpeg", fo);
} catch(exception e) {
e.printstacktrace();
throw new runtimeexception("failed in create preview image. error: " + e.getmessage());
}
}
十三、实现rss
rss是一个标准的xml文件,rss阅读器可以读取这个xml文件获得文章的信息,使用户可以通过rss阅读器而非浏览器阅读blog,我们只要动态生成这个xml文件便可以了。rsslibj是一个专门读取和生成rss的小巧实用的java库,大小仅25k,可以从http://sourceforge.net/projects/rsslibj/下载rsslibj-1_0rc2.jar和它需要的exmljar两个文件,然后复制到web/web-inf/lib/下。
使用rsslibj异常简单,我们先设置好httpservletresponse的header,然后通过rsslibj输出xml即可:
channel channel = new channel();
channel.setdescription(account.getdescription());
baseurl = baseurl.substring(0, n);
channel.setlink("http://server-name/home.c?accountid=" + accountid);
channel.settitle(account.gettitle());
list articles = facade.getarticles(accountid, account.getmaxperpage(), 1);
iterator it = articles.iterator();
while(it.hasnext()) {
article article = (article)it.next();
channel.additem("http://server-name/article.c?articleid=" + article.getarticleid(),
article.getsummary(), article.gettitle()
);
}
// 输出xml:
response.setcontenttype("text/xml");
printwriter pw = response.getwriter();
pw.print(channel.getfeed("rss"));
pw.close();
十四、实现全文搜索
全文搜索能大大方便用户快速找到他们希望的文章,为blog增加一个全文搜索功能是非常必要的。然而,全文搜索不等于sql的like语句,因为关系数据库的设计并不是为全文搜索设计的,数据库索引对全文搜索无效,在一个几百万条记录中检索like '%a%'可能会耗时几分钟,这是不可接受的。幸运的是,我们能使用免费并且开源的纯java实现的lucene全文搜索引擎,lucene可以非常容易地集成到我们的blog中。
lucene不提供直接对文件,数据库的索引,只提供一个高性能的引擎,但接口却出人意料地简单。我们只需要关心以下几个简单的接口:
document:代表lucene数据库的一条记录,也代表搜索的一条结果。
field:一个document包含一个或多个field,类似关系数据库的字段。
indexwriter:用于创建新的索引,也就是向数据库添加新的可搜索的大段字符串。
analyzer:将字符串拆分成单词(token),不同的文本对应不同的analyzer,如htmlanalyzer,pdfanalyzer。
query:封装一个查询,用于解析用户输入。例如,将“bea blog”解析为“同时包含bea和blog的文章”。
searcher:搜索一个query,结果将以hits返回。
hits:封装一个搜索结果,包含document集合,能非常容易地输出结果。
下一步,我们需要为article表的content字段建立全文索引。首先为lucene新建一个数据库,请注意这个数据库是lucene专用的,我们不能也不必知道它的内部结构。lucene的每个数据库对应一个目录,只需要指定目录即可:
string indexdir = "c:/search/blog";
indexwriter indexwriter = new indexwriter(indexdir, new standardanalyzer(), true);
indexwriter.close();
然后添加文章,让lucene对其索引:
string title = "文章标题"
// 从数据库读取
string content = "文章内容"
// 从数据库读取
// 打开索引:
indexwriter indexwriter = new indexwriter(indexdir, new standardanalyzer(), false);
// 添加一个新记录:
document doc = new document();
doc.add(field.keyword("title", title));
doc.add(field.text("content", content));
// 建立索引:
indexwriter.adddocument(doc);
// 关闭:
indexwriter.close();
要搜索文章非常简单,然后添加文章,让对其索引:
string title = "文章标题" // 从数据库读取
string content = "文章内容" // 从数据库读取
// 打开索引:
indexwriter indexwriter = new indexwriter(indexdir, new standardanalyzer(), false);
// 添加一个新记录:
document doc = new document();
doc.add(field.keyword("title", title));
doc.add(field.text("content", content));
// 建立索引:
indexwriter.adddocument(doc);
// 关闭:
indexwriter.close();
要搜索文章:
searcher searcher = new indexsearcher(dir);
query query = queryparser.parse(keyword, "content", new standardanalyzer());
hits hits = searcher.search(query);
if(hits != null){
for(int i = 0;i < hits.length(); i++){
document doc = hits.doc(i);
system.out.println("found in " + doc.get("title"));
system.out.println(doc.get("content"));
}
}
searcher.close();
我们设计一个lucenesearcher类封装全文搜索功能,由于必须锁定数据库所在目录,我们把数据库设定在/web-inf/search/下,确保用户不能访问,并且在配置文件中初始化目录:
<bean id="lucenesearcher" class="org.crystalblog.search.lucenesearcher">
<property name="directory">
<value>/web-inf/search/</value>
</property>
</bean>
十五、发送email
blog用户可以让系统将来访用户的留言发送到注册的email地址,为了避免使用smtp发信服务器,我们自己手动编写一个sendmail组件,直接通过smtp协议将email发送到用户信箱。
sendmail组件只需配置好dns服务器的ip地址,即可向指定的email信箱发送邮件。并且,sendmail使用缓冲队列和多线程在后台发送email,不会中断正常的web服务。具体代码请看sendmail.java。
十六、测试
服务器配置为:p4 1.4g,512m ddr,100m ethernet,windows xp professional sp2。
测试服务器分别为weblogic server 8.1,tomcat 4.1/5.0,resin 2.1.1。
测试数据库为ms sql server 2000 sp3。
十七、中文支持
测试发现,中文不能在页面中正常显示,为了支持中文,首先在web.xml加入filter,用于将输入编码设置为gb2312:
<filter>
<filter-name>encodingfilter</filter-name>
<filter-class>org.crystalblog.web.filter.encodingfilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>gb2312</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingfilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
然后用文本工具搜索所有的.htm,.html,.properties文件,将“iso-8859-1”替换为“gb2312”,现在页面中文已经能正常显示,但是lucene仍不能正常解析中文,原因是标准的standarda?nalyzer只能解析英文,可以从网上下载一个支持中文的analyzer。
十八、总结
spring的确是一个优秀的j2ee框架,通过spring强大的集成和配置能力,我们能轻松设计出灵活的多层j2ee应用而无需复杂的ejb组件支持。
闽公网安备 35060202000074号