1. 程式人生 > >IE瀏覽器下載檔案中文檔名亂碼問題解決

IE瀏覽器下載檔案中文檔名亂碼問題解決

在web開發工作當中,瀏覽器相容性問題總是會引來一大波前端攻城獅們的瘋狂吐槽,尤其是IE瀏覽器更是一個神一般的存在,總是讓人頭疼不已。
前段時間用java在做一個檔案上傳下載功能時,發現部分IE瀏覽器上下載中文檔名檔案時會出現檔名亂碼的現象。經過檢視之前呼叫的兩個檔案下載工具類原始碼發現其中一個工具類原來程式碼是直接使用ISO8859-1編碼對檔名進行編碼,而另一個工具類則多了一層篩選,通過對瀏覽器請求頭中的User-Agent引數中是否存在MISE關鍵字對進行了判斷,當用戶使用瀏覽器是IE時使用URLEncoder.encode(name,”UTF-8”)函式對檔名進行了編碼,主要程式碼如下:

//第一個工具類中的下載方法:
    /**
     * 下載
     * @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();
    }

大家如果遇到類似問題,可以參考!☺☺☺