1. 程式人生 > >Java Web中的中文編碼問題(下)

Java Web中的中文編碼問題(下)

Java Web 涉及到的編碼

      對於使用中文來說,有 I/O 的地方就會涉及到編碼,前面已經提到了 I/O 操作會引起編碼,而大部分 I/O 引起的亂碼都是網路 I/O,因為現在幾乎所有的應用程式都涉及到網路操作,而資料經過網路傳輸都是以位元組為單位的,所以所有的資料都必須能夠被序列化為位元組。在 Java 中資料被序列化必須繼承 Serializable 介面。

      使用者從瀏覽器端發起一個 HTTP 請求,需要存在編碼的地方是 URL、Cookie、Parameter。伺服器端接受到 HTTP 請求後要解析 HTTP 協議,其中 URI、Cookie 和 POST 表單引數需要解碼,伺服器端可能還需要讀取資料庫中的資料,本地或網路中其它地方的文字檔案,這些資料都可能存在編碼問題,當 Servlet 處理完所有請求的資料後,需要將這些資料再編碼通過 Socket 傳送到使用者請求的瀏覽器裡,再經過瀏覽器解碼成為文字。

URL 的編解碼

瀏覽器編碼 URL 是將非 ASCII 字元按照某種編碼格式編碼成 16 進位制數字然後將每個 16 進製表示的位元組前加上“%”。

HTTP Header 的編解碼

當客戶端發起一個 HTTP 請求除了上面的 URL 外還可能會在 Header 中傳遞其它引數如 Cookie、redirectPath 等,這些使用者設定的值很可能也會存在編碼問題,Tomcat 對它們又是怎麼解碼的呢?
    對 Header 中的項進行解碼也是在呼叫 request.getHeader 是進行的,如果請求的 Header 項沒有解碼則呼叫 MessageBytes 的 toString 方法,這個方法將從 byte 到 char 的轉化使用的預設編碼也是 ISO-8859-1,而我們也不能設定 Header 的其它解碼格式,所以如果你設定 Header 中有非 ASCII 字元解碼肯定會有亂碼。
   我們在新增 Header 時也是同樣的道理,不要在 Header 中傳遞非 ASCII 字元,如果一定要傳遞的話,我們可以先將這些字元用org.apache.catalina.util.URLEncoder 編碼然後再新增到 Header 中,這樣在瀏覽器到伺服器的傳遞過程中就不會丟失資訊了,如果我們要訪問這些項時再按照相應的字符集解碼就好了。

POST 表單的編解碼

在前面提到了 POST 表單提交的引數的解碼是在第一次呼叫 request.getParameter 發生的,POST 表單引數傳遞方式與 QueryString 不同,它是通過 HTTP 的 BODY 傳遞到服務端的。當我們在頁面上點選 submit 按鈕時瀏覽器首先將根據 ContentType 的 Charset 編碼格式對錶單填的引數進行編碼然後提交到伺服器端,在伺服器端同樣也是用 ContentType 中字符集進行解碼。所以通過 POST 表單提交的引數一般不會出現問題,而且這個字符集編碼是我們自己設定的,可以通過 request.setCharacterEncoding(charset) 來設定。

      另外針對 multipart/form-data 型別的引數,也就是上傳的檔案編碼同樣也是使用 ContentType 定義的字符集編碼,值得注意的地方是上傳檔案是用位元組流的方式傳輸到伺服器的本地臨時目錄,這個過程並沒有涉及到字元編碼,而真正編碼是在將檔案內容新增到 parameters 中,如果用這個編碼不能編碼時將會用預設編碼 ISO-8859-1 來編碼

HTTP BODY 的編解碼

當用戶請求的資源已經成功獲取後,這些內容將通過 Response 返回給客戶端瀏覽器,這個過程先要經過編碼再到瀏覽器進行解碼。這個過程的編解碼字符集可以通過 response.setCharacterEncoding 來設定,它將會覆蓋 request.getCharacterEncoding 的值,並且通過 Header 的 Content-Type 返回客戶端,瀏覽器接受到返回的 socket 流時將通過 Content-Type 的 charset 來解碼,如果返回的 HTTP Header 中 Content-Type 沒有設定 charset,那麼瀏覽器將根據 Html 的 <meta HTTP-equiv="Content-Type" content="text/html; charset=GBK" /> 中的 charset 來解碼。如果也沒有定義的話,那麼瀏覽器將使用預設的編碼來解碼。

綜述,出現亂碼問題就是因為在從char到byte或者從byte到char的轉換中編碼和解碼的字符集不一致導致的。但是一次操作涉及多次編解碼,所以遇到問題的時候需要根據具體問題具體分析。