#
PNG圖片作為網頁圖的一個好處是它允許有透明背景,這一點比gif格式做得要好,但也有不如意的地方,就是在FireFox上PNG的透明背景顯示得很好,但是在IE中就做不到了,這就需要濾鏡技術的幫助。
比如有一張圖片作為logo,它放在一個id為logodiv的DIV中,在CSS中是這樣定義的:
#logoDiv{
width:300px;
height:100%;
background:transparent url(../img/logo.png) no-repeat -65px 0px;
}
這張圖片在FF顯示沒有問題,在IE6中就需要加上這樣一段:
*html #logoDiv{
width:300px;
height:100%;
margin-left:-65px;
background-color:transparent;
background-image:none;
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src="web/img/logo.png", sizingMethod="crop");
}
這段代碼FireFox是不認識的,會被略過,圖片會繼續上面#logoDiv的效果,而IE會用下面一段取代上面這一段,所以濾鏡透明圖片的效果就出現了。這兩段代碼放在一起,無論客戶瀏覽器是IE6或是FireFox都能適應了。
使用濾鏡有兩點需要注意的:一是圖片地址是網頁相對app的地址,不是css相對app的地址,所以上面兩處路徑會有差異;另外一點是在不同的IE濾鏡的寫法也有差異,需要根據客戶瀏覽器實際情況調整一下。
具體代碼請見,其中有三處使用了濾鏡,分別是logo,主菜單左側和右側,您可以從下面的代碼中去尋找它們
http://www.box.net/shared/tz5k1um68l
http://www.box.net/shared/mbdf2e85yi
說明:下文涉及的內容只限于對前端jsp和后臺servlet的代碼修改,不涉及Web容器和數據庫的相關修改。
我的測試環境是:英文XP操作系統,容器采用Tomcat6,前臺是jsp,后臺Servlet,Ajax框架采用prototype1.6.0
Web編程中前后端亂碼問題發生的根源:
從前端jsp到后臺的Servlet,中間傳輸過程中的默認編碼方式是ISO-8859-1,這種編碼方式是西歐字符集,包括英語,德語,丹麥語,芬蘭語等,其它語言如中文日文等是不兼容的,如不經轉換或是設置直接在Servlet中用request.getParameter(paramName)這種方式直接拿出來中文部分就會得到亂碼,但英語,字母,數字組合成的字符串是不會變成亂碼的。
常見的編碼解決亂碼問題的方式有:
- new String(request.getParameter(paramName).getBytes("ISO-8859-1"),"頁面字符集");
- HttpServletRequest.setCharacterEncoding("頁面字符集");
- URLDecoder.decode(str, "UTF-8");
它們各自用在不同的場合,這里的頁面字符集指GBK,GB2312,UTF-8等,它們通常和jsp頁面設定的字符集(charset)一致。
如果是將form通過post方式提交,在servlet的doPost函數開頭(對于Struts寫在execute函數開頭)寫上request.setCharacterEncoding("jsp網頁字符集")就可以直接用request.getParameter(paramName)直接得到文字,不需要經過再轉碼。這里需要注意的是form的action如果不直接寫成post的話,是會以get方式提交,這時request.setCharacterEncoding就會失效。
如果不管是post還是get方式,想做一個通用方案,則可以通過new String(request.getParameter(paramName).getBytes("ISO-8859-1"),"jsp網頁編碼方式")得到轉碼后正常的文字,這種方式只要知道網頁的字符集,幾乎都能還原成正確的文字,適用性很廣,在Servlet和Action中很常見。
如果是Ajax提交方式,則在servlet或是action中書寫request.setCharacterEncoding("jsp網頁字符集")是沒有效果的,經過對提交的URL用一次javascript的encodeURI函數編碼后,用 new String(request.getParameter(paramName).getBytes("ISO-8859-1"),"jsp網頁字符集")可以得到正確的文字。但如果不用encodeURI函數處理提交的url則不會正確的文字。
Ajax提交中文的另一種方式可以用javascript的encodeURI函數對提交的URL進行兩次編碼,而后臺采用URLDecoder.decode(request.getParameter(paramName), "UTF-8")得到正確的文字。
上面兩種方式都借助了avascript的encodeURI函數的幫助,它能將字符串進行utf-8編碼,其中,第二種方式確定性很高,推薦。
具體方式請參考下面給出的例程,里面含有12個例子,分別對應了多種情況:
http://www.tkk7.com/Files/heyang/DisorderCode2011-01-31.zip
參考文章:
深入淺出 web 編碼(轉載整理)
http://www.tkk7.com/heyang/archive/2011/01/26/343570.html
以下代碼主體來自互聯網,原作者已經不可考,在此向這些有共享精神的作者致敬。
一.日期的合法性校驗
以下代碼中進行驗證的主函數是isValidDateString,它的輸入是類似2011-1-27這樣的字符串,如果合法就返回真,否則返回假。如果您的日期格式不同,請將驗證主函數中的正則表達式修改之,比如說輸入是2011/1/27這樣的字符串,則pattern=/^ *(\d{4})[/](\d{1,2})[/](\d{1,2})*$/;
// 進行日期合法性驗證的主函數
function isValidDateString(dateString){
var pattern=/^ *(\d{4})-(\d{1,2})-(\d{1,2})*$/;
var arr=pattern.exec(dateString);
var year=arr[1];
var month=arr[2];
var dday=arr[3];
return IsValidDate(year,month,dday);
}
function IsValidYear(psYear)
{
var sYear = new String(psYear);
if(psYear==null)
{
return false;
}
if(isNaN(psYear)==true)
{
return false;
}
if(sYear == "")
{
return true;
}
if(sYear.match(/[^0-9]/g)!=null)
{
return false;
}
var nYear = parseInt(sYear, 10);
if((nYear < 0) || (9999 < nYear))
{
return false;
}
return true;
}
function IsValidMonth(psMonth)
{
var sMonth = new String(psMonth);
if(psMonth==null)
{
return false;
}
if(isNaN(psMonth)==true)
{
return false;
}
if(sMonth == "")
{
return true;
}
if(sMonth.match(/[^0-9]/g)!=null)
{
return false;
}
var nMonth = parseInt(sMonth,10);
if((nMonth < 0) || (12 < nMonth))
{
return false;
}
return true;
}
function IsValidDay(psDay)
{
var sDay = new String(psDay);
if(psDay==null)
{
return false;
}
if(isNaN(psDay)==true)
{
return false;
}
if(sDay == "")
{
return true;
}
if(sDay.match(/[^0-9]/g)!=null)
{
return false;
}
var nDay = parseInt(psDay, 10);
if((nDay < 0) || (31 < nDay))
{
return false;
}
return true;
}
function IsValidDate(psYear, psMonth, psDay)
{
if(psYear==null || psMonth==null || psDay==null)
{
return false;
}
var sYear = new String(psYear);
var sMonth = new String(psMonth);
var sDay = new String(psDay);
if(IsValidYear(sYear)==false)
{
return false;
}
if(IsValidMonth(sMonth)==false)
{
return false;
}
if(IsValidDay(sDay)==false)
{
return false;
}
var nYear = parseInt(sYear, 10);
var nMonth = parseInt(sMonth, 10);
var nDay = parseInt(sDay, 10);
if(sYear=="" && sMonth=="" && sDay=="")
{
return true;
}
if(sYear=="" || sMonth=="" || sDay=="")
{
return false;
}
if(nMonth < 1 || 12 < nMonth)
{
return false;
}
if(nDay < 1 || 31 < nDay)
{
return false;
}
if(nMonth == 2)
{
if((nYear % 400 == 0) || (nYear % 4 == 0) && (nYear % 100 != 0))
{
if((nDay < 1) || (nDay > 29))
{
return false;
}
}
else
{
if((nDay < 1) || (nDay > 28))
{
return false;
}
}
}
else if((nMonth == 1) ||
(nMonth == 3) ||
(nMonth == 5) ||
(nMonth == 7) ||
(nMonth == 8) ||
(nMonth == 10) ||
(nMonth == 12))
{
if((nDay < 1) || (31 < nDay))
{
return false;
}
}
else
{
if((nDay < 1) || (30 < nDay))
{
return false;
}
}
return true;
}
二.日期的比較
下面函數是進行日期比較如果date1小于等于date2,則返回真,否則返回假。注意這兩個參數都應該通過了上面的日期合法性校驗,請注意先驗證一下。
function isReasonable(startDate,endDate){
startDate=startDate.replace("-","/");
endDate=endDate.replace("-","/");
var dt1=new Date(Date.parse(startDate));
var dt2=new Date(Date.parse(endDate));
return dt1<=dt2;
}
在Ajax程序中,在URL拼接時帶有中文參數是不可避免的事情,如
var url='/YourAppName/CreateTodo.do?name='+name;
其中name是來自inputbox的取值,它可能帶有中文。
如果讓Ajax直接提交這樣的URL,那么后臺用request.getParameter("name");這樣的方法得到的name就會含有亂碼。
解決之道是先對URL進行兩次編碼,用的是JavaScript的encodeURI函數,具體代碼如下:
var url=encodeURI('/YourAppName/CreateTodo.do?name='+name);
url=encodeURI(url);
在后臺的Servlet或是Action中,可以這樣得到正確的文字:
Sting name=java.net.URLDecoder.decode(request.getParameter("name"),"utf-8");
就是這樣,值得注意的是,中文環境的機器用容器跑WebApp也許不需要這樣的處理,但其它環境如日文,英文就非此不可了,因此在編碼時盡可能這樣處理一下,如果安裝后出現問題就會造成慌亂了。我們在編寫Web程序時,最好讓文件編碼,數據庫編碼,輸出編碼,網頁編碼保持一致,這樣能省去很多麻煩。
以上操作的具體原理請見:
http://yiminghe.javaeye.com/blog/243812
http://yiminghe.javaeye.com/blog/247837
已有功能:
進行混合加密的消息傳遞,用戶注冊,登錄,取RSA公鑰,得到用戶列表,通過服務器中轉消息。
修正點:
1.針對較長文字改變了解析方式。
2.聊天界面又向QQ靠攏了一些。
下載地址:
http://www.tkk7.com/Files/heyang/IMSample2011-01-25-1320.zip
界面截圖:
一般說的組合鍵,是指在按下某個特定的鍵的時候,有另一些鍵處于某個特定的狀態。例如:按回車enter,且CTRL鍵處于按下的狀態,就認為是按了CTRL+回車這個組合鍵。
下面是具體的代碼,myTextArea是一個文本區域組件(JTextArea)。注意其中粗體部分:
myTextArea.addKeyListener(new KeyListener(){
@Override
public void keyReleased(KeyEvent arg0) {
if ( arg0.getKeyCode() == KeyEvent.VK_ENTER &&
((arg0.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) &&
((arg0.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) == 0) ) {
// do something......
}
}
@Override
public void keyPressed(KeyEvent arg0) {
// do nothing
}
@Override
public void keyTyped(KeyEvent arg0) {
// do nothing
}
});
上面粗體部分第一句意味著回車鍵處于按下狀態;
第二句意味著同時Ctrl鍵處于按下狀態;
第三句意味著Shift鍵沒有處于按下狀態;
整個條件就是指在ctrl+enter鍵按下時,執行特定的處理。
Java文本組件中檢測組合鍵就是這樣簡單。
下載地址:
http://www.box.net/shared/ccymfosmyu
本版改善功能:
1.客戶端界面朝QQ靠近了點。
2.去除了發送信息時的一個潛在隱患。
myFrame.setExtendedState(JFrame.NORMAL);
myFrame.toFront();
myFrame是從Swing的JFame繼承而來的類。
// 設定布局
int gridx, gridy, gridwidth, gridheight, anchor, fill, ipadx, ipady;
double weightx, weighty;
GridBagConstraints c;
Insets inset;
GridBagLayout gridbag = new GridBagLayout();
this.setLayout(gridbag);
// 0,0
gridx = 0;
gridy = 0;
gridwidth = 1;
gridheight = 1;
weightx = 1.00;
weighty = 1.00;
anchor = GridBagConstraints.CENTER;
fill = GridBagConstraints.BOTH;
inset = new Insets(up, left, down, right);
ipadx = 0;
ipady = 0;
c = new GridBagConstraints(gridx, gridy, gridwidth, gridheight,
weightx, weighty, anchor, fill, inset, ipadx, ipady);
JScrollPane js=new JScrollPane(msgArea);
gridbag.setConstraints(js, c);
this.add(js);
以上代碼中,Insets構造函數四個參數的順序依次為上,左,下,右, 逆時針方向。這樣比較好記憶。
Swing中其它類似的四參數形式(如BorderFactory.createEmptyBorder(top, left, down, right))也類同此例。
將文字在網絡中進行傳輸的時候,如果存在非ASCII碼字符,很容易出現亂碼問題,要解決也不難,在傳輸的文字上用URLEncoder進行編碼,將它變成全部是ASCII碼的形式,這樣在網絡傳輸中就不會受到影響;在另一側,將收到的文字用URLDecoder加碼就能還原文字原本的摸樣。
IMSample中涉及到文字的混合加密,情況稍復雜一點,但流程還是一樣的。
相關涉及編碼和解碼的工具類:
package com.heyang.common.code;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
/**
* UTF8轉碼器
* @author heyang
*
*/
public class UTF8Coder{
private static final String UTF_8 = "utf-8";// 編碼形式
/**
* 對文字進行UTF8轉碼
* @param str
* @return
*/
public static String encode(String str){
try {
return URLEncoder.encode(str, UTF_8);
} catch (UnsupportedEncodingException e) {
return null;
}
}
/**
* 將轉碼后的文字還原
* @param str
* @return
*/
public static String decode(String str){
try {
return URLDecoder.decode(str, UTF_8);
} catch (UnsupportedEncodingException e) {
return null;
}
}
}
變化后的加密器代碼:
package com.heyang.common.cipher;
import org.apache.commons.codec.binary.Base64;
import com.heyang.common.code.AESSecurityCoder;
import com.heyang.common.code.Base64SecurityUtil;
import com.heyang.common.code.RSASecurityCoder;
import com.heyang.common.code.UTF8Coder;
/**
* 對消息進行加密的加密器
* 說明:
* 作者:何楊(heyang78@gmail.com)
* 創建時間:2010-12-27 下午07:00:29
* 修改時間:2010-12-27 下午07:00:29
*/
public class IMMsgEncrypter{
// 經加密的消息
private String cipheredMsg;
/**
* 構造函數
* @param plainMsg 未加密的消息
* @param otherSideRSAPublicKey 對方RSA公鑰
* @param rsaCoder 己方RSA編碼器
* @param aesCoder 己方AES編碼器
* @throws IMMsgEncryptException
*/
public IMMsgEncrypter(String plainMsg,String otherSideRSAPublicKey,RSASecurityCoder rsaCoder,AESSecurityCoder aesCoder) throws IMMsgEncryptException{
try{
// 防止亂碼
plainMsg=UTF8Coder.encode(plainMsg);
// 對明文進行AES加密
byte[] aesArr=aesCoder.getEncryptByteArray(plainMsg); // 對明文進行AES加密
String cipherText=Base64.encodeBase64String(aesArr);// 得到AES加密后的密文
// 使用RSA對AES密鑰進行加密
String key=aesCoder.getAesKey();// 取得AES的密鑰
String aesKey="";
try{
byte[] clientRsaKeyArr=null;
clientRsaKeyArr=Base64.decodeBase64(otherSideRSAPublicKey);
byte[] rsaArr=rsaCoder.getEncryptArray(key, clientRsaKeyArr);
aesKey=Base64.encodeBase64String(rsaArr);
}
catch(Exception ex){
throw new IMMsgEncryptException("使用對方RSA公鑰加密己方AES鑰匙時發生異常.");
}
// 在發出的密文前附帶經服務器RSA公鑰加密的AES密鑰
StringBuilder sb=new StringBuilder();
sb.append("<aeskey>"+aesKey+"</aeskey>");
sb.append("<rsakey>"+rsaCoder.getPublicKeyString()+"</rsakey>");
sb.append("<text>"+cipherText+"</text>");
// 最后對整體進行Base64加密
cipheredMsg=Base64SecurityUtil.getEncryptString(sb.toString());
}
catch(Exception ex){
throw new IMMsgEncryptException("加密消息時發生異常,異常信息為"+ex.getMessage()+".");
}
}
public String getCipheredMsg() {
return cipheredMsg;
}
}
修改后的解碼器代碼:
package com.heyang.common.cipher;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.codec.binary.Base64;
import com.heyang.common.code.AESSecurityCoder;
import com.heyang.common.code.Base64SecurityUtil;
import com.heyang.common.code.RSASecurityCoder;
import com.heyang.common.code.UTF8Coder;
/**
* 消息解密器
* 說明:
* 作者:何楊(heyang78@gmail.com)
* 創建時間:2010-12-27 下午07:41:44
* 修改時間:2010-12-27 下午07:41:44
*/
public class IMMsgDecrypter{
// 固定的三個節點名
private static final String TEXT = "text";
private static final String RSAKEY = "rsakey";
private static final String AESKEY = "aeskey";
// 對方的RSA公鑰
private String otherSideRSAPublicKey;
// 解密后的明文
private String plainMsg;
/**
* 構造函數
* @param cipherMsg 要解密的消息
* @param rsaCoder 己方RSA編碼器
* @param aesCoder 己方AES編碼器
* @throws IMMsgDecryptException
*/
public IMMsgDecrypter(String cipherMsg,RSASecurityCoder rsaCoder,AESSecurityCoder aesCoder) throws IMMsgDecryptException{
try{
// 先用Base64解密密文
cipherMsg=Base64SecurityUtil.getDecryptString(cipherMsg);
// 用正則表達式得到密鑰文,客戶端的RSA公鑰和密文
String regex="<(\\w+)>((.|\\s)+)</\\1>";
Pattern pattern=Pattern.compile(regex);
Matcher matcher=pattern.matcher(cipherMsg);
String cipheredAesKey="";// 經服務器RSA公鑰加密的客戶端AES鑰匙密文
String cipherText="";// 經客戶端AES加密的密文
Map<String,String> map=new HashMap<String,String>();
while(matcher.find()){
map.put(matcher.group(1), matcher.group(2));
}
if(map.size()==3){
cipheredAesKey=map.get(AESKEY);
otherSideRSAPublicKey=map.get(RSAKEY);
cipherText=map.get(TEXT);
}
else{
throw new IMMsgDecryptException("解密消息時發生異常,原因是消息格式不正確.消息為:"+cipherMsg);
}
// 得到經過服務器RSA私鑰解密后的AES密鑰
String plainAesKey="";
try {
byte[] cipheredAesKeyArr=Base64.decodeBase64(cipheredAesKey);
plainAesKey=rsaCoder.getDecryptString(cipheredAesKeyArr);
} catch (Exception e) {
throw new IMMsgDecryptException("無法解密對方AES密鑰,異常信息為"+e.getMessage()+",客戶端請求為:"+cipherMsg);
}
// 使用AES密鑰解密出明文
byte[] cipherTextArr=Base64.decodeBase64(cipherText);
plainMsg=aesCoder.getDecryptString(cipherTextArr, plainAesKey);
// UTF08還原
plainMsg=UTF8Coder.decode(plainMsg);
}
catch(Exception ex){
throw new IMMsgDecryptException("解密消息發生異常,異常信息為"+ex.getMessage()+".");
}
}
public String getOtherSideRSAPublicKey() {
return otherSideRSAPublicKey;
}
public String getPlainMsg() {
return plainMsg;
}
}
以上只是涉及亂碼問題的一個處理方法,各位還要具體情況具體分析。
|