文件下载(IE)时文件名损坏
我已经实现了一种简单的文件上载-下载机制。用户单击文件名时,将使用以下HTTP标头下载文件:
HTTP/1.1 200 OK Date: Tue, 30 Sep 2008 14:00:39 GMT Server: Microsoft-IIS/6.0 Content-Disposition: attachment; filename=filename.doc; Content-Type: application/octet-stream Content-Length: 10754
我也支持日语文件名。为此,我使用以下java方法对文件名进行编码:
private String encodeFileName(String name) throws Exception{ String agent = request.getHeader("USER-AGENT"); if(agent != null && agent.indexOf("MSIE") != -1){ // is IE StringBuffer res = new StringBuffer(); char[] chArr = name.toCharArray(); for(int j = 0; j < chArr.length; j++){ if(chArr[j] < 128){ // plain ASCII char if (chArr[j] == '.' && j != name.lastIndexOf(".")) res.append("%2E"); else res.append(chArr[j]); } else{ // non-ASCII char byte[] byteArr = name.substring(j, j + 1).getBytes("UTF8"); for(int i = 0; i < byteArr.length; i++){ // byte must be converted to unsigned int res.append("%").append(Integer.toHexString((byteArr[i]) & 0xFF)); } } } return res.toString(); } // Firefox/Mozilla return MimeUtility.encodeText(name, "UTF8", "B"); }
到目前为止,它一直运行良好,直到有人发现它不能与长文件名一起使用。例如:2008.10.1.doc
。如果我将一个单字节点更改为一个单字节下划线,或者删除了第一个字符,则可以正常工作。即取决于点字符的长度和URL编码。
以下是一些示例。
这是坏的(2008.10.1.doc
):
Content-Disposition: attachment; filename=%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%822008%2E10%2E1%e3%81%82.doc;
可以((2008_10.1.doc)):
Content-Disposition: attachment; filename=%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%822008_10%2E1%e3%81%82.doc;
这也很好(2008.10.1.doc
):
Content-Disposition: attachment; filename=%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%82%e3%81%822008%2E10%2E1%e3%81%82.doc;
有人知道吗?
解决方案
gmail处理文件名转义的方式有所不同:文件名加引号(双引号),单字节句点不转义URL。
这样,问题中的长文件名就可以了。
Content-Disposition: attachment; filename="%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%82%E3%81%822008.10.1%E3%81%82.doc"
但是,文件名的字节长度仍然存在限制(显然仅限于IE)(我认为是一个错误)。因此,即使文件名仅由单字节字符组成,文件名的开头也会被截断。
限制约为160个字节。
此处的主要问题是IE不支持相关的RFC(此处为RFC2231)。请参阅指针和测试用例。此外,用于IE的解决方法(仅使用转义百分比的UTF-8)还有其他一些问题。它可能无法在所有语言环境中都起作用(据我所知,除非将IE配置为始终在URL中使用UTF-8(这不是默认设置),否则该方法在韩国会失败),并且如前所述,存在长度限制(听说IE8中已修复该问题,但我还没有尝试过)。
如上所述,如果没有浏览器嗅探并为每个浏览器返回不同的标头,则Content-Disposition和Unicode不可能使所有主要浏览器都能正常工作。
我的解决方案是完全避免使用Content-Disposition标头,而将文件名添加到URL的末尾,以欺骗浏览器以为它是直接获取文件。例如
http://www.xyz.com/cgi-bin/dynamic.php/あああああああああああああああ2008.10.1あ.doc
这自然假定我们在创建链接时就知道文件名,尽管快速重定向标头可以根据需要进行设置。
我认为此问题已在IE8中修复,我已经看到它在IE 8中有效。