十二、生成缩略图 当用户上传了图片后,必须生成缩略图以便用户能快速浏览。我们不需借助第三方软件,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组件支持。
|