中文名文件下载浏览器兼容处理
前言
文件下载功能中的文件名处理,这又是个老生长谈的问题了,网络上也有各种解决方式,但可能由于各自项目语言以及编码不同导致多数方案都不尽如人意,最近又遇到这个问题,姑且根据自己的环境和编码总结一下
后续观点都是基于如下环境
- 服务端语言:JAVA
- 项目编码:GBK
- 应用服务器Resin 3.1
分析
Firefox会截断空格问题
在Firefox下,如果文件名中有空格,那么空格后面部分的文字会被浏览器截取掉,客户端获取不到完整的文件名
解决方案:
- 将文件名使用双引号包裹起来
- 使用 MimeUtility.encodeWord(filename)方法,将其编码为“=?gb2312?B?xxxxxxxx?=”格式,其中的空格会被编码掉,并且Firefox可以识别该格式将其还原为原始的文件名
IE6下请求头长度有限制,文件名长度只能在150字节左右
经过测试,IE6下当要下载的文件名长度超过150字节后,超过部分会被浏览器截取掉(为了保证扩展名完整,浏览器会从前面截取),并且如果是中文还有可能乱码(应该是由于编码后再截取后不能再次还原为中文字符),这个是IE6自身的机制,因此我们只能考虑使用较短的文件名或者自己使用代码合理将其截取
关于乱码
这一块我也不是理解的十分透彻,就我的理解简单分析一下:
首先,大家都知道中文是多字节字符,由于计算机的基本存储单元是字节(byte),而一个byte显然不能够表示包括中文在内的所有字符,因此中文、日文等等语言在存储时都会被编码为多个字节存储到计算机中,然后显示时在相应解码显示即可。
然后,网络传输的单位也是字节,因此java中的字符串在传输之前如果不进行手工编码那么会被默认按照操作系统的编码格式进行编码,到客户端再按照默认格式解码,如果都是中文环境通常没有问题,但是如果两边的默认编码不一致就比较容易导致编码,因此还是建议手动指定特定的编码和解码格式。
而对于文件下载,客户端不同浏览器识别的编码也不同,但是基本上都识别UTF-8编码以及ISO-8859-1编码,后者firefox不能识别,并且如果文件名不是全中文还会导致被截断,因此我还是选择采用UTF-8编码
最后,贴上代码
由于其它功能都被拆分出去了,就不贴了,核心部分代码都在这里。
下面的代码在IE6-9、firefox、chrome测试都可以正常下载
String us = request.getHeader("user-Agent"); if(ua!=null){ ua = ua.toLowerCase(); //IE6截取文件名避免乱码 if(ua.contains("msie 6")){ //文件名中有空格,encode后会变成+,为让其正确显示空格将其替换为%20
//getSubStr方法是根据字节数截取长度超过150字节后的字符 filename = URLEncoder.encode(getSubStr(filename),"utf-8").replaceAll("\\+","20%"); }else if(ua.contains("firefox")){ //编码成 =?gb2312?B?xxxxxxxx?=的格式,firefox支持这种格式,这里尽量指定编码类型,否则可能解析不了 filename = MimeUtility.encodeWord(filename,"utf-8","B");
response.setHeader("Content-disposition", "attachment; filename=\"+filename+\"");
}else{
filename = URLEncoder.encode(filename,"utf-8").replaceAll("\\+","20%"); } } response.setContentType("application/x-download;"); response.addHeader("Content-Disposition",filename) OutputStream out = response.getOutputStream(); out.write(contents); out.flush()
参考资料:http://stackoverflow.com/questions/93551/how-to-encode-the-filename-parameter-of-content-disposition-header-in-http