String filename="測試";
String client = request.getHeader("user-agent").toLowerCase();
if (client.indexOf("msie") >= 0) {
filename = java.net.URLEncoder.encode(filename, "utf-8");
} else {
filename = javax.mail.internet.MimeUtility.encodeText(filename,"utf-8","B");
}
response.setContentType("APPLICATION/OCTET-STREAM");
response.addHeader("Content-Disposition", "attachment; filename="+filename+".exe");
OutputStream os = response.getOutputStream();
File f = new File ("C:/Downloads/core JavaServer™ Faces.chm");
FileInputStream isf = new FileInputStream(f);
InputStreamReader isr = new InputStreamReader(isf);
BufferedReader rb = new BufferedReader(isr);
byte[] b = new byte[10240];
int i = 0;
while (isf.read(b) != -1) {
os.write(b);
}
os.close();
通過此頁面進行文件上傳后, IE, NC以及FF所傳輸的數據均相同. 如下
Content-Type: multipart/form-data; boundary=---------------------------282302224217945
Content-Length: 27980
-----------------------------282302224217945
Content-Dis;form-data; name="theText"
C:\縺ゅ≠縺ゅ≠.xls
-----------------------------282302224217945
Content-Dis;form-data; name="theFile"; filename="縺ゅ≠縺ゅ≠.xls"
Content-Type: application/vnd.ms-excel
可以看出, 對text box和file upload box中的文件名所有瀏覽器均采取了相同的編碼. 經證實, 是上傳頁面的編碼方式——所有瀏覽器均對unicode數據(utf-8)采取了本地的編碼方式(這里是ms932).
在服務器端對上傳的數據進行解碼.
解碼的方式有很多, 這里我使用最普遍以及正規的request.setCharacterEncoding的方法. 發現form表單中的text box可以被正常解碼, 但是file upload box中的文件名無法通過這種方式解碼. 所以只能使用手工解碼.
String fixedFileName = new String(fileName.getBytes("SJIS"), "UTF-8");
其中SJIS是客戶端系統的編碼, UTF-8是客戶端頁面的編碼.
上傳文件測試中, 所有瀏覽器表現一致, 需要注意的是文件名和表單數據的不同處理方式.
下載文件
使用一個unicode的JSP頁面, 在頁面上有一個固定的超鏈接, 傳遞給服務器一個文件名. 服務器依照這個文件名把服務器端的文件傳遞給客戶端.
下載頁面
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<a href="download.do?name=ああああ.xls">ダウンロード</a>
對于這樣一個頁面, 當點擊超鏈接后, 各瀏覽器處理方式不同
IE會把超鏈接依照頁面當前編碼方式編碼(這里是utf-8)后, 發送給服務器端
GET /nsupload/download.do?name=縺ゅ≠縺ゅ≠.xls HTTP/1.1
NC和FF會把超鏈接依照頁面編碼方式編碼(這里是utf-8)后, 再通過url encoding后, 發送給服務器端
GET /nsupload/download.do?name=%E3%81%82%E3%81%82%E3%81%82%E3%81%82.xls HTTP/1.1
(經證實, E38182是「あ」的unicode代碼)
在服務器收到提交的數據后, 需要對其進行解碼. 需要注意的是這種方式下使用request.setCharacterEncoding無效. 所以必須手工解碼.
name = new String(name.getBytes("ISO-8859-1"), "UTF-8");
其中ISO-8859-1是Tomcat服務器的特性, Tomcat會把所有的數據先轉換為ISO-8859-1的形式. UTF-8是實際的編碼方式.
在得到文件名后, 就可以正確地讀取文件, 然后把文件傳遞給客戶端了. 其中, 文件名是保存在Http報頭(header)的Content-Disposition中.
response.setHeader("Content-Disposition", "inline; filename=" + _filename);
//或者
response.setHeader("Content-Disposition", "attachment; filename=" + _filename);
經實驗證明, 使用inline或者attachment對文件名的編碼方式
沒有影響.
另外一個需要設置的是Content-Type.
response.setContentType("application/vnd.ms-excel");
//或者
response.setContentType("application/vnd.ms-excel; charset=UTF-8");
//或者
response.setContentType("application/x-download; >" + _filename);
經試驗證明, 使用application/*的任何形式都對文件名的編碼方式
沒有影響.
第二點, 經試驗證明, 這里的charset設置會被三種瀏覽器忽略, 所以設置與否
不影響文件名的編碼方式.
第三點, 經試驗證明, 這里的name設置對文件名
沒有任何影響.
可能還有一個屬性需要注意, 就是Content-Language. 經試驗證明, Content-Language有無, 或者為何值, 對文件名沒有任何影響.
那么對于non-ascii的文件名如何操作才可以保證客戶端可以得到正確的顯示呢?
經過調查, 有三種方法(在網上搜索后認為可能這篇文章是對于這個問題探討最深入的文章)
第一, 使用URLEncoding方法. 即對文件名進行URLEncoding.
name = URLEncoder.encode(name, "UTF-8");
這種方式適用于IE, 但是不適用于NC和FF. 在這種方式下, 網絡上傳輸的是url encoding后的ascii編碼.
Content-Dis;inline; filename=%E3%81%82%E3%81%82%E3%81%82%E3%81%82.xls
NC和FF不能對這樣的文件名進行有效的解碼
第二, 使用字符串字符集強行轉換為本地字符集方法, 這樣做的原理是JVM底層全部為unicode. 所以一旦一個字符串表示了正確的字符集而被存儲后, 這個字符串會被轉換為任意字符集.
原理二是, IE和FF對非url encoding的non-ascii文件名采用客戶端系統本地的編碼方式進行轉換.
name = new String(name.getBytes("Shift_JIS"), "ISO-8859-1");
需要注意的是, 這里的name原本是utf-8的.
在網絡上傳輸的為
Content-Dis;inline; filename=ああああ.xls
經過試驗, IE和FF支持這種方式, NC不支持. 表現為NC無法解析文件名.
第三種, 使用Base64編碼文件名. 原理是這種做法符合RFC2047的定義.
name = javax.mail.internet.MimeUtility.encodeText(name, "UTF-8", "B");
使用到了JavaMail中的Base64編碼的類MimeUtility.
在網絡上傳輸的為經過Base64編碼的ascii字符.
Content-Dis;inline; filename==?UTF-8?B?44GC44GC44GC44GCLnhscw==?=
只有FF支持這種方式, IE表現為無法解析文件名, NC表現為忽略Base64編碼.
以上三種方法是目前來講, 使瀏覽器可以正確下載non-ascii文件名的方法. 其中IE支持兩種(url encoding和force transform), FF支持兩種(force transform和base64 encoding), NC一種都不支持.
關于這次調查的結果, 對于NC多說兩句, 我以為這個結果是由于NC 7.1和Tomcat 5.5不兼容造成的. Tomcat 5.5要求必須把所有報頭先轉變為ISO-8859-1的格式, 而NC 7.1卻無法直接對ISO-8859-1進行正確的解析或者說是解析功能比較弱. 如果有時間, 我會繼續驗證非unicode的情況以及NC 8的情況.
---2006年9月14日21:00 補充---
在NC 8.1上進行了測試, 測試結果是NC 8.1支持方法三, 即base64 encoding.
</script>