因为不同浏览器对head处理的行为不同,所以下载对话框中的中文文件名比较麻烦.没有统一方法,不要以为你看到的是正确的中文,换个浏览器
或换个版本就不一定正确了.
大多数情况下,默认转出UTF-8的字符串是可以的:
String str = "这是一个UTF-8格式编码的中文字符串.dat";
如果不是UTF-8,那么转换到 UTF-8的字符串,大多数时候是有效的.
但是同样是UTF-8字符串,IE的不同版本看到的竟然是不同效果.虽然在contentTyep中已经指明 charset=UTF-8,而且从
客户端进看页面编码也是UTF-8,但不同版本的IE却有的乱码有的正常.
对于IE而言,全部使用url-encode,所有版本都是正确的.这是网上流行的解决方案,但是.....................
对于IE6,限制文件名大约在150个字符以内,一个中文字符UTF8编码为3个byte,再URLEncode成9个byte,所以对于IE6最多不能超过17个中文.所以这个方法不行.
使用俺8年前用的IE下载的乱码无敌大法,当时在IE6以下的所有版本都绝不乱码.今天测试了一下,IE7,IE8,IE9BATE也坚决不乱,又不会有17个中文字符的限制.方法:
将字符串以GB18030编码转换为byte[],然后将每个byte置的成char进行UTF8编码:
byte[] src = "UTF8字符串".getBytes("GB18030");
byte[] buf = new byte[src.length * 3];
int pos = 0;
for(int i=0;i<src.length;i++){
char c = (char)src[i] & 0xFF;
if(c <= 0x007F && c != 0) buf[pos++] = (byte)c;
else if(c > 0x07FF){
buf[pos+2] = (byte) (0x80 | ((c >> 0 ) & 0x3F));
buf[pos+1] = (byte) (0x80 | ((c >>6 ) & 0x3F));
buf[pos+0] = (byte) (0xE0 | ((c >>12 ) & 0x0F));
pos += 3;
}
else{
buf[pos+1] = (byte) (0x80 | ((c >>0) & 0x3F));
buf[pos+0] = (byte) (0xC0 | ((c >>6 ) & 0x1F));
pos += 2;
}
}
return new String(buf,0,pos,"UTF-8");
绝对无敌....................
但是对于FF,url-encode肯定是不行了.直接输出UTF-8,基本上测试过的版本都正确.
但是...................................
不同的容器行为又不样.因为response.setCharacterEncoding只对实体起作用,对头域不起作用,所以对于头域只能手工处理.
比如tomcat,因为它默认是ISO8859_1,你通过response.setCharacterEncoding可以让它对实体部分的处理按你指定的字符集
处理,但
response.setHeader("Content-Disposition","attachment;filename="+str);
无法控制str按指定的字符集处理,所以只能反向转换把UTF-8转成ISO8859_1:
str = new String(str.getBytes("UTF-8"),"ISO8859_1");
response.setHeader("Content-Disposition","attachment;filename="+str);
这样在tomcat输出后,大多数浏览器接收到的 byte[]是UTF-8格式的,能正确处理(IE还是存在版本问题),但这种方案又和容器偶合了.
如果FF不和容器偶合的话,可以进行base64编码,就象IE进行urlencode编码一样.
我KAO,这不是折腾人吗?
那么要尽量做到通用,先要判断浏览器:
String agent = request.getHeader("User-Agent");
if(agent == null) xxxx;
agent = agent.toUpperCase();
if(agent.indexOf("MSIE") != -1) str = URLEncoder.encode(str);
else if(agent.indexOf("FIREFOX") != -1) str = Base64Encoder.encode(str);
else{
............................
}
余下的浏览器怎么办呢?经过测试Opera/Chrome都只能解释UTF-8的中文名.所以
else{
............................
}将这样:
else if(serverTyep.equals("tomcat")){
str = new String(str.getBytes("UTF-8"),"ISO8859_1");
}
else{
//其它容器是否要转码看它默认用什么字符集处理.比如resin就不用任何编码,只要是UTF-8格式的就正确
}
对于服务端的不兼容,其它平台不一定有这个问题.但是对于客户端的不兼容,任何平台都会有这个问题.要针对不同浏览器做不同的处理.