程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> SpringSide開發實戰(五):兵馬未動,糧草先行

SpringSide開發實戰(五):兵馬未動,糧草先行

編輯:關於JAVA

本篇討論的問題是對項目中遇到的難題進行技術穿刺。

做過項目的人都知道,在構思完一個項目的功能之後,緊接著的事情就是考慮這些構思 的功能如何實現,對於自己不熟悉的領域,要進行技術穿刺。我的穿刺方法為先查找有無比 較好的開源組件可用,如果沒有,就查找相關的文檔,自己編寫和測試代碼。

在這一篇,我主要解決三個問題。

1、解決字符串加密的問題,在前面一篇中,我們設計用戶模塊的時候,准備將用戶的密 碼字段以MD5加密的方式保存,因此,這裡需要寫一個對字符串加密生成MD5字符串的方法;

2、解決生成圖像縮略圖和生成驗證碼的問題;

3、解決url重寫的問題,之所以要用到url重寫,主要是為了讓用戶在訪問自己的主頁時 ,可以使用http://www.xkland.com/username或者http://username.xkland.com這樣的形式 ,而不是像http://www.xkland.com/index.jsp?username=xxx這樣難看的形式。

需要說明的是,要解決上面的三個問題,不是沒有開源的東西可用,而是我覺得每次都 要整合不同的組件是在是太麻煩,而我們需要的功能也不是很復雜,我們不需要太通用的東 西,只要能夠解決這裡特定的問題就行了,因此不如自己動手實現,同時還可以獲得技術上 的提高。

首先來看看MD5加密的問題,JDK中本來提供有數據加密的支持,其中 java.security.MessageDigest類就可以實現MD5的加密,但是,加密後生成的數據是byte[] 類型的,這裡只需要寫一個方法將它轉換為字符串就行,代碼如下:

package com.xkland.util;
import java.security.MessageDigest;
import java.lang.NullPointerException;
import java.security.NoSuchAlgorithmException;
public  class StringUtil  {
  public  static  char [] num_chars =  new  char [] { ' 0 ' ,
  ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' , ' 5 ' , ' 6 ' , ' 7 ' , ' 8 ' ,
  ' 9 ' , ' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F ' } ;
  public  static String toMD5String(String input)
  throws NullPointerException,NoSuchAlgorithmException {
  if (input ==  null ) throw  new NullPointerException();
  char [] output =  new  char [ 32 ];
  MessageDigest md = MessageDigest.getInstance( " MD5 " );
  byte [] by = md.digest(input.getBytes());
  for ( int i = 0 ;i < by.length;i ++ ) {
  output[ 2 * i] = num_chars[ (by[i] &  0xf0 ) >>  4 ];
  output[ 2 * i + 1 ] = num_chars[ by[i] &  0xf ];
  }
  return  new String(output);
}
}

下面是它的測試用例:

package com.xkland.util;
import junit.framework.TestCase;
public  class StringUtilTest extends TestCase  {
  public  void testToMD5String()  {
  try {
  System.out.println(StringUtil.toMD5String( " abc " ));
  } catch (Exception e) {
  }
}
}

運行測試用例,輸出結果為:

900150983CD24FB0D6963F7D28E17F72

再來說說關於圖像縮略圖生成的問題,我准備將它設置為一個可以讓Spring管理的類, 簡單的說,可以利用Spring的配置文件來設置該類的一些屬性,比如原圖像保存的目錄和目 標圖像保存的目錄,生成的縮略圖的大小,生成縮略圖的方式。這裡特別需要說明的就是這 個生成縮略圖的方式,我們即可以指定它只簡單的執行縮放,也可以指定它進行剪裁以後再 縮放。為什麼要這麼設計,請大家看看如下的效果圖,對於下面這兩張美女圖:

如果我們只通過簡單的縮放來生成縮略圖,那麼在網頁上的布局效果為:

如果我們通過先剪切後縮放的效果來生成縮略圖,那麼在網頁上布局的效果為:

可以看到通過第二種方式生成的縮略圖布局要漂亮一些,但是會損失圖片的信息 。因此,兩種方式各有優劣。所以在設計的時候就設計為能夠讓用戶靈活配置。

對 於有些網友反映的gif動畫經過縮放以後就不能動了,這個問題的主要原因是因為Java SDK 1.4和1.5版本的ImageIO類只能讀gif格式的文件,而不能寫gif格式的文件,因此,對於gif 格式的文件,生成的縮略圖只能用png格式代替,在我的設計中,我准備讓bmp格式的文件也 讓png格式代替,因為png格式生成的文件更小,而且也不損失圖片質量。至於Java SDK 1.4 和1.5版不支持寫gif格式的文件,可以查看Java文檔,下面是截圖:

最新推 出的Java SDK 6是可以寫gif格式的文件的,因此如果要解決這個問題,可以使用最新的JDK ,下面是文檔截圖:

下面是 我寫的生成縮略圖和生成驗證碼的ImageUtil類的源代碼:

package  

com.xkland.util;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.awt.Image;
import java.awt.Graphics2D;
import java.util.Random;
import java.awt.Font;
import javax.servlet.http.HttpSession;
public class ImageUtil {
private String sourceDir; // 圖片的存放路徑
private String destinationDir; // 縮略圖的存放路徑
private String mode; // 生成縮略 圖的模式,可選ScaleOnly或ClipAndScale
private String width; // 縮略圖 的寬度
private String height; // 縮略圖的高度
private String characterStorage; // 用來生成驗證碼的字符倉庫
// 以下代碼段是為了 使用Spring注入屬性
public void setCharacterStorage(String characterStorage) {
this .characterStorage = characterStorage;
}
public void setDestinationDir(String destinationDir) {
this .destinationDir = destinationDir;
}
public void setHeight(String height) {
this .height = height;
}
public void setMode(String mode) {
this .mode = mode;
}
public void setSourceDir(String sourceDir) {
this .sourceDir = sourceDir;
}
public void setWidth(String width) {
this .width = width;
}
// 生成縮略圖的方法,默認縮略圖的文件名和原圖相同,存放路徑不同
public void createMicroImage(String fileName)
throws Exception {
// 判斷sourceDir的格式是否為以"\"結尾,並生成 完整的路徑
String sourceFileName;
String destinationFileName;
if (sourceDir.lastIndexOf( ' \\ ' ) != (sourceDir.length() - 1 )) {
sourceFileName = sourceDir + " \\ " + fileName;
destinationFileName = destinationDir + " \\ " + fileName;
} else {
sourceFileName = sourceDir + fileName;
destinationFileName = destinationDir + fileName;
}
// 創建文件,並判斷原文件是否存在
File sourceFile = new File (sourceFileName);
if ( ! sourceFile.exists()) {
throw new Exception();
}
// 根據擴展名判斷原文件的格 式
String extension = fileName.substring(fileName.lastIndexOf( ' . ' ) + 1 );
if ( ! extension.equalsIgnoreCase( " jpg " ) && ! extension.equalsIgnoreCase( " bmp " )
&& ! extension.equalsIgnoreCase( " gif " ) && ! extension.equalsIgnoreCase( " png " )) {
throw new Exception();
}
// 判斷縮略圖的寬度和高度是否正確,如果不 能正確解析則拋出異常
int destinationWidth = Integer.parseInt (width);
int destinationHeight = Integer.parseInt(height);

// 判斷縮放模式是否正確,如果配置錯誤,則拋出異常
if ( ! mode.equalsIgnoreCase( " ScaleOnly " )
&& ! mode.equalsIgnoreCase( " ClipAndScale " )) {
throw new Exception();
}
// 讀取圖像文件,並創 建BufferedImage對象,如果不能讀取,則拋出異常
BufferedImage image = null ;
image = ImageIO.read(sourceFile);
if (image == null ) {
throw new Exception();
}
// 獲取原圖像文件的高度和寬度
int sourceWidth = image.getWidth ();
int sourceHeight = image.getHeight();
// 生成縮略 圖
if (mode.equalsIgnoreCase( " ScaleOnly " )) {
BufferedImage destinationImage;
if (( float ) sourceWidth / destinationWidth > ( float )sourceHeight / destinationHeight) {
Image tempImage = image.getScaledInstance (destinationWidth, ( int )(destinationWidth * (( float )sourceHeight / sourceWidth)), Image.SCALE_DEFAULT);
destinationImage = new BufferedImage(destinationWidth, ( int )(destinationWidth * (( float ) sourceHeight / sourceWidth)),BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = destinationImage.createGraphics();
graphics.drawImage(tempImage, 0 , 0 , null );

} else {
Image tempImage = image.getScaledInstance(( int )(destinationHeight * (( float )sourceWidth / sourceHeight)), destinationHeight, Image.SCALE_DEFAULT);
destinationImage = new BufferedImage(( int )(destinationHeight * (( float )sourceWidth / sourceHeight)), destinationHeight,BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = destinationImage.createGraphics();
graphics.drawImage(tempImage, 0 , 0 , null );
}
// 如果是bmp或者gif,則縮略圖為png格式
if (extension.equalsIgnoreCase( " bmp " ) || extension.equalsIgnoreCase( " gif " )) {
extension = " png " ;
destinationFileName = destinationFileName.substring( 0 , destinationFileName.lastIndexOf( ' . ' )) + " . " + extension;
}
File destinationFile = new File (destinationFileName);
ImageIO.write(destinationImage, extension, destinationFile);
} else {
BufferedImage destinationImage;
if (( float )sourceWidth / destinationWidth > ( float )sourceHeight / destinationHeight) {
// 先裁 減
int x = sourceWidth - ( int )(sourceHeight * (( float )destinationWidth / destinationHeight));
Image clipedImage = image.getSubimage(( int )( 0.5 * x), 0 , ( int )(sourceHeight * (( float )destinationWidth / destinationHeight)), sourceHeight);
// 後縮放
Image scaledImage = clipedImage.getScaledInstance(destinationWidth, destinationHeight, Image.SCALE_DEFAULT);
destinationImage = new BufferedImage(destinationWidth, destinationHeight,BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = destinationImage.createGraphics();
graphics.drawImage(scaledImage, 0 , 0 , null );
} else {
// 先裁減
int y = sourceHeight - ( int ) (sourceWidth * (( float )destinationHeight / destinationWidth));
Image clipedImage = image.getSubimage( 0 , ( int )( 0.5 * y), sourceWidth, ( int )(sourceWidth * (( float )destinationHeight / destinationWidth)));
// 後縮放
Image scaledImage = clipedImage.getScaledInstance(destinationWidth, destinationHeight, Image.SCALE_DEFAULT);
destinationImage = new BufferedImage(destinationWidth, destinationHeight,BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = destinationImage.createGraphics();
graphics.drawImage(scaledImage, 0 , 0 , null );
}
// 如果是bmp或者gif,則縮略圖為png格式
if (extension.equalsIgnoreCase( " bmp " ) || extension.equalsIgnoreCase( " gif " )) {
extension = " png " ;
destinationFileName = destinationFileName.substring( 0 , destinationFileName.lastIndexOf( ' . ' )) + " . " + extension;
}
File destinationFile = new File (destinationFileName);
ImageIO.write(destinationImage, extension, destinationFile);
}
}
// 生成驗證碼的方法
public BufferedImage createValidateImage(HttpSession session) {
BufferedImage validateImage = new BufferedImage( 80 , 20 ,BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = validateImage.createGraphics();
// 從characterStorage中隨機抽取四個 字符生成驗證碼
int length = characterStorage.length();
char [] chars = new char [ 4 ];
Random rand = new Random ();
for ( int i = 0 ; i < 4 ; i ++ ) {
int index = rand.nextInt(length);
chars[i] = characterStorage.charAt(index);
}
String str = new String(chars);
// 將字符串保存到Session中,以便於驗證
session.setAttribute( " validateString " , str);
// 畫字符 串到圖片中
graphics.setFont( new Font( " 宋體 " ,Font.BOLD, 18 ));
graphics.drawString(str, 2 , 16 );
// 隨機畫干擾直線
for ( int i = 0 ; i < 5 ; i ++ ) {
int x1 = rand.nextInt( 80 );
int y1 = rand.nextInt( 20 );
int x2 = rand.nextInt( 80 );
int y2 = rand.nextInt( 20 );
graphics.drawLine(x1, y1, x2, y2);
}
return validateImage;
}
}

寫得比較倉促,沒有進行重構,所以比較難看一點。下面是測試用例的代碼:

package  com.xkland.util;
import junit.framework.TestCase;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
public class ImageUtilTest extends TestCase {
public void testCreateMicroImage() throws Exception {
ImageUtil util = new ImageUtil();
util.setSourceDir( " E:\\ " );
util.setDestinationDir( " F:\\ " );
util.setWidth( " 100 " );
util.setHeight( " 100 " );
// 以僅縮放的形式生成縮略圖
util.setMode( " ScaleOnly " );
// 橫圖像
util.createMicroImage( " 001.bmp " );
// 豎圖像
util.createMicroImage( " 002.jpg " );
// 以先裁減後縮放的 形式生成縮略圖
util.setDestinationDir( " G:\\ " );
util.setMode( " ClipAndScale " );
// 橫圖像
util.createMicroImage( " 001.bmp " );
// 豎圖像
util.createMicroImage( " 002.jpg " );
}
public void testCreateValidateImage() throws Exception {
ImageUtil util = new ImageUtil();
util.setCharacterStorage( " ABCDEFGHIJKLMNOPQRSTUVWXYZ北冥有魚其名為鲲鲲之大不知其幾千裡也化而為鳥其名為鵬鵬 之背不知其幾千裡也怒而飛其翼若垂天之雲是鳥也海運則將徙於南冥南冥者天池也 " );
BufferedImage image = util.createValidateImage();
ImageIO.write(image, " jpg " , new File( " F:\\validateImage.jpg " ));
}
}

運行該測試用例,可以成功的生成縮略圖,並且可以生成驗證碼,生成的驗證碼如下圖 :

把以上代碼再修改再完善,就可以創建更漂亮一點的圖形了。

為了把上面這個ImageUtil類讓SpringSide管理起來,並進行靈活的配置,可以在 src\main\resources\spring目錄下建立beans.xml文件,並如下配置:

<? xml version="1.0" encoding="UTF-8" ?> 
<! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd" >
< beans >
< bean id ="imageUtil" class ="com.xkland.util.ImageUtil" >
< property name ="sourceDir" >
< value > E:\ </ value >
</ property >
< property name ="destinationDir" >
< value > F:\ </ value >
</ property >
< property name ="width" >
< value > 100 </ value >
</ property >
< property name ="height" >
< value > 100 </ value >
</ property >
< property name ="mode" >
< value > ScaleOnly </ value >
</ property >
< property name ="characterStorage" >
< value > ABCDEFGHIJKLMNOPQRSTUVWXYZ北冥有魚其名為鲲鲲之大不知其幾千裡也化而為鳥其名為鵬鵬 之背不知其幾千裡也怒而飛其翼若垂天之雲是鳥也海運則將徙於南冥南冥者天池也 </ value >
</ property >
</ bean >
</ beans >

最後,我們再來看看url重寫的問題。俗話說得好:“會者不難,難者不會”,剛開始我 為了實現文章開頭所說的url重寫功能,嘗試采用的是配置Servlet映射的方法,但是怎麼都 不成功,後來才想到使用Filter來實現。有時候開源的東西會直接影響人的思路,比如 Struts 1.x采用的就是配置Servlet映射的方法,而到了2.0,也改成Filter了。

在Filter中實現url重寫比較簡單,無非就是分析字符串和替換字符串,這裡我就不列代 碼了。只有想不到,沒有做不到,想辦法實現我們設計的功能,這便是技術穿刺的作用。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved