#
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.blogjava.net/Files/heyang/DisorderCode2011-01-31.zip
参考文章:
深入浅出 web 编码(转载整理)
http://www.blogjava.net/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.blogjava.net/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;
}
}
以上只是涉及乱码问题的一个处理方法,各位还要具体情况具体分析。
|