處理過程
根據IE的F12中的log提示,是因為http頭信息中的編碼替換了html文件中的編碼。我最初的思路是設置Tomcat默認編碼,但是我發現我已經在Server.xml中設置過,想到這里我想到了上篇文章的文件重命名的問題,准備去寫個filter去修改http響應頭,但是我突然明白,SpringMVC應該會處理過,但是為什么無效呢,這種成熟的框架不應該會有bug存在,於是我嘗試在SpringMVC的xml中配置SourceHttpMessageConverter等轉換器,都無效。
發現原因
后來發現web.xml是配置了編碼處理的, 內容如下:
//第一個工具類中的下載方法:
/**
* 下載
* @param request
* @param response
* @param fileName
* @param downLoadPath
* @param contentType
* @throws IOException
* @throws Exception
*/
public static void download(HttpServletRequest request,HttpServletResponse response,String fileName,String downLoadPath,String contentType) throws IOException{
response.setContentType("text/html;charset=UTF-8");
request.setCharacterEncoding("UTF-8");
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
long fileLength = new File(downLoadPath).length();
response.setContentType(contentType);
response.setHeader("Content-disposition", "attachment; filename=" + new String(fileName.getBytes("utf-8"), "ISO8859-1"));
response.setHeader("Content-Length", String.valueOf(fileLength));
bis = new BufferedInputStream(new FileInputStream(downLoadPath));
bos = new BufferedOutputStream(response.getOutputStream());
byte[] buff = new byte[2048];
int bytesRead;
while(-1 != (bytesRead = bis.read(buff, 0, buff.length))){
bos.write(buff, 0, bytesRead);
}
bis.close();
bos.close();
}
//第二個工具類中的主要關鍵代碼:
String filename = null;
if(request.getHeader("User-Agent").toUpperCase().indexOf("MSIE") > 0){
filename = URLEncoder.encode(name, "UTF-8");
}else{
filename = new String(name.getBytes(),"ISO8859-1");
}
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
為了知道這樣子到底會有什么問題,是不是只有IE會出現問題,其他瀏覽器會不會有其他的問題。抱着這些疑問,我找來了目前市面上用戶量較多的幾款主流的瀏覽器進行測試,包括:兩個不同版本的IE11,Chrome,FireFox,Opera,360瀏覽器,搜狗瀏覽器等。經過我對下載的文件的各種文件名稱使勁地折騰主要發現以下幾個問題:
1.兩個IE11在使用第一個方法下載文件時中文文件名都會亂碼,而使用第二個方法下載時其中一個IE11中文不會亂碼,另一個IE11則會亂碼;
2.文件名中存在空格時兩個IE11瀏覽器下載下來文件文件名空格會變成+號,其他瀏覽器沒有這個問題;
3.火狐瀏覽器下載時遇到文件名中有空格時下載下來的文件的文件名第一個空格后面的文字都會丟失。
經過一番折騰發現原來我們大國產瀏覽器還是可以的,沒有發現明顯的問題;233333。
那么這些問題該如何解決呢?首先第一個問題,顯然第一段代碼並沒有將IE瀏覽器過濾出來對文件名進行單獨編碼,而是統一采用ISO8859-1編碼,這樣如果文件名是英文的IE就不會出現任何問題,中文就會出現亂碼,其他語言文字則不清楚,其他幾款瀏覽器則沒有任何問題,都能兼容。那第二段代碼則首先對Http請求頭中User-Agent參數進行了判斷,通過MISE字段將IE瀏覽器過濾出來,對文件名進行單獨UTF-8編碼,所以中文文件名不會亂碼了。那么問題來了,為什么兩個IE11瀏覽器,其中一個中文不亂碼,而另一個卻亂碼呢。經過對程序的單步調試,發現那個中文亂碼的IE在進入這個方法后程序並沒有跳入對文件名進行utf-8編碼的方法,而是進入了else下面的那一行代碼。那也就是說兩個IE11的User-Agent參數中,其中一個有MISE關鍵字,而另一個則沒有。通過查閱資料,原來微軟在IE11之后在瀏覽器的User-Agent參數中去掉了MISE關鍵字,導致的結果是使用低版本的IE下載中文文件名文件時不會亂碼,而采用大部分IE11及以上版本,包括Edge等都會出現中文亂碼現象。難怪大家老是說IE很坑呢,IE在這種地方都埋好了坑,等着我們去踩,我也是醉了。
瀏覽器的User-Agent這個參數主要包含了一些操作系統版本,瀏覽器版本、內核等信息。
那個下載文件中文會亂碼的IE的User-Agent參數如下:
IE11:Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko;
顯然里面並沒有MISE關鍵字,那么怎么辦呢?通過對比幾款不同瀏覽器的User-Agent參數,發現這里面有一個Trident關鍵字比較特別,是其他瀏覽器所沒有的,那么解決方案來了,我們可以通過Trident關鍵字來進行對部分IE11的過濾了;
那么針對第二個問題中出現的空格變+號,則是因為URLEncoder函數在對字符串進行轉碼后將空格替換成了+號,IE就直接把+號顯示出來了,解決方法是在對文件名進行轉碼后,使用replace方法將+號替換為%20即可,瀏覽器會將%20轉換成空格輸出。
對於第三個問題則是因為代碼在set響應頭時Content-Disposition參數的attachment;filename=等號后面文件名字符串沒有用雙引號括起來,火狐瀏覽器對於遇到文件名有空格時認為空格前的字符是一個完整的字符串,故下載下來文件時文件名就只剩下空格前的那幾個字了。解決方法是在filename兩邊加上雙引號並加反斜杠轉義。具體的最終解決這些問題后經過測試兼容性比較好的代碼如下:
public static void download(HttpServletRequest request, HttpServletResponse response, String fileName, String downLoadPath, String contenType) throws Exception {
response.setContentType("text/html;charset=UTF-8");
request.setCharacterEncoding("UTF-8");
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
long fileLength = new File(downLoadPath).length();
response.setContentType(contenType);
String header = request.getHeader("User-Agent").toUpperCase();
if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
fileName = URLEncoder.encode(fileName, "utf-8");
fileName = fileName.replace("+", "%20"); //IE下載文件名空格變+號問題
} else {
fileName = new String(fileName.getBytes(), "ISO8859-1");
}
response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + "\"");
response.setHeader("Content-Length", String.valueOf(fileLength));
bis = new BufferedInputStream(new FileInputStream(downLoadPath));
bos = new BufferedOutputStream(response.getOutputStream());
byte[] buff = new byte[2048];
int bytesRead;
while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
bis.close();
bos.close();
}
或者,使用下面這段代碼也可以處理亂問題,代碼如下:
try {
String fileName = attachmentFile.originalName();
String userAgent = request.getHeader("user-agent").toLowerCase();
if (userAgent.contains("msie") || userAgent.contains("like gecko") ) {
// win10 ie edge 瀏覽器 和其他系統的ie
fileName = URLEncoder.encode(fileName, "UTF-8");
} else {
//其他的瀏覽器
fileName = new String(fileName.getBytes("utf-8"), "iso-8859-1");
}
response.setCharacterEncoding("utf-8");
response.setContentType("multipart/form-data");
response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
String path = attachmentFile.path();
InputStream inputStream = new FileInputStream(new File(path));
OutputStream os = response.getOutputStream();
byte[] b = new byte[2048];
int length;
while ((length = inputStream.read(b)) > 0) {
os.write(b, 0, length);
}
// 這里主要關閉流。
os.close();
inputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
獲取更多精彩內容,學習資料,視頻等,請關注微信公眾號【程序員Style】,回復關鍵字即可。