Tomcat/JSP中文編碼配置

長風破浪發表於2015-03-19

來源:http://blog.csdn.net/zhangzikui/article/details/6169978

        http://www.iteye.com/topic/300656

 

第一  java原始碼檔案編碼

儲存檔案必須以一種編碼存;讀檔案也必須以一種編碼讀,如不特別設定,去系統預設的編碼,中文windows為GBK編碼。
     從.java->.class過程是,先編寫.java檔案並按某種編碼方式儲存,然後用javac方法編譯此檔案,注意如.java沒按系統預設編碼儲存則要帶encoding引數指明實際編碼,否則出錯,生成的.class檔案存為系統預設編碼。
     從.jsp->.java->.class,先存為某種編碼的.jsp檔案,然後tomcat根據pageEncoding讀取並轉化為servlet存為系統預設編碼,然後同上面.java->.class過程。
第二eclipse開發環境對原始碼檔案編碼的影響

IDE的encoding為對系統下檔案開啟的解碼方式或儲存的編碼方式。特例:如果.jsp檔案有<%@ page language="java" pageEncoding="UTF-8"%>,則eclipse會自動存為UTF-8方式,不管eclipse的encoding是什麼,這也是eclipse的聰明之處。
第三 jsp原始碼檔案編碼
pageEncoding="UTF-8"表示此檔案的編碼方式,必須與此檔案儲存方式一致(所以eclipse會首選根據它來存檔案),

<%@ page language="java" pageEncoding="UTF-8"%>  表示JSP檔案編碼方式,web容器tomcat根據這個來讀此.jsp檔案並編譯為servlet。

第四  servlet生成html檔案編碼方式

<%@ page contentType="text/html;charset= “UTF-8"%>
contentType="text/html;charset=UTF-8"表示當瀏覽器得到此檔案時以什麼方式解碼。例:

 

<%@ page language="java" pageEncoding="UTF-8"%>
<%@ page contentType="text/html;charset=iso8859-1"%>
<html>
<head>
<title>test</title>
</head>
<body>
我是個好人
</body>
</html>

會產生亂碼,因為存為UTF-8的檔案被解碼為iso8859-1,這樣 如有中文肯定出亂碼。
至此,頁面應為:
 

<%@ page language="java" pageEncoding="UTF-8"%>
<%@ page contentType="text/html;charset=UTF-8"%>
<html>
<head>
<title>中文問題</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
我是個好人
</body>
</html>


第四,requestresponse編碼設定方式
request.setCharacterEncoding("UTF-8")是把提交內容的字符集設為UTF-8
response.setCharacterEncoding("UTF-8")可以把頁面中的<%@ page contentType="text/html;charset=iso8859-1"%>

第五,java連線資料庫編碼 

1、mysql配置檔案:
修改mysql在windows/my.ini裡default-character-set=utf-8
2、mysql裡資料庫和表也都設為utf8_unicode_ci
3、資料庫連結:jdbc:mysql://localhost/mydb?useUnicode=true&characterEncoding=utf-8
注意,關鍵就在於此:此句中間是'&'不是'&'這是因為資料庫連結時,在.jsp和.java檔案中應該用&號,而XML檔案中需要用&
對於Web容器來說,如果你不設定,預設是ISO8859-1
String des = new String(s.getBytes("iso8859-1"),"UTF-8");都可以使用這個 不論哪裡,有亂碼就是用

第六頁面請求的編碼(表單的get提交、表單的post提交、頁面連結傳遞中文引數、位址列中引數直接輸入中文提交 

請求響應的全過程:

瀏覽器 IE/FireFox ----------->Servlet容器------------------------>顯示頁面

       編碼                           使用容器的URIEncoding轉碼              解碼

換為charset=UTF-8,是給告訴瀏覽器我這個檔案的編碼方式。
一共四種方式:表單的get提交、表單的post提交、頁面連結傳遞中文引數、位址列中引數直接輸入中文提交。

無論何種表單提交都可以在後臺的java檔案中通過String des = new String(s.getBytes("iso8859-1"),"UTF-8");來轉換成你想要的UTF-8編碼方式。

1.     表單get方式提交

         瀏覽器根據頁面的charset編碼方式對頁面進行編碼,然後提交至伺服器,首先進入對應的字元編碼過濾器(如果有的話),不過Tomcat6.0對於get提交方式採用的是server.xml檔案中的URIEncoding編碼方式,而並不會採用過濾器中設定的編碼,那麼根據我的環境設定,jsp頁面都使用UTF-8的編碼,Servlet容器的URIEncoding也設定為UTF-8,則servlet不用進行轉碼即可正確解碼,獲得正常的中文字串。那麼,響應頁面的中文因為頁面的統一編碼(UTF-8)自然也會正常顯示。當然,如果我們Tomcat的URIEncoding設定為其他非UTF-8的編碼方式時,頁面的內容進入Tomcat解析時,因為Tomcat和頁面的編碼不統一,就需要轉碼。例如,如果我們採用Tomcat預設的ISO-8859-1,那麼當我們使用request.getParameter("yourVariable ")獲取表單引數值時其實Servlet就進行了轉碼,方式為UTF-8-->ISO-58859-1(我的頁面charset都是UTF-8),類似於這樣

new String(變數值.getBytes("UTF-8"),"ISO-8859-1");  

例如表單的username屬性以字串"編輯"提交,那麼進入容器後,FormBean中的這個變數會亂碼,request.getParameter(username)一樣的效果,s1就是request返回的結果,下面是記憶體快照。
  

不過即使這樣,我們依然可以使用不恰當的方法顯示正常的中文,即逆向轉碼,例如上面的亂碼,我們可以通過ISO8859-1-->UTF-8這種方式還原我們提交時的中文。以下是GBK,UTF-8,ISO-8859-1三者之間互相轉換的記憶體快照:
   

我們可以看到,偶數漢字可以在UTF-8,GBK兩者中互相轉換,而奇數個漢字則不能。綜上看來,貌似Tomcat的URIEncoding設定為UTF-8是最好的解決辦法,不過這樣的設定依然無法解決上面我所說的第三、第四種情況。大家繼續向下看。(這裡有一點我不確定,就是頁面提交至Servlet容器時,是以頁面的charset方式編碼後直接進入容器,還是以charset轉碼為ISO-8859-1方式進入,大家有什麼見解?)
2.表單的post提交

對於這種方式的請求,request.setCharacterEncoding("一般來自於web.xml中過濾器設定的引數")方法進行編碼設定將會產生作用,struts的表單提交方式預設為post方式,那麼按照上面我的環境設定,頁面,容器,都採用UTF-8編碼方式,就不會產生中文亂碼問題。

程式加上org.springframework.web.filter.CharacterEncodingFilter過濾器. 

<filter> 
<filter-name>encodingFilter</filter-name> 
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 
<init-param> 
<param-name>encoding</param-name> 
<param-value>UTF8</param-value> 
</init-param> 
<init-param> 
<param-name>forceEncoding</param-name> 
<param-value>true</param-value> 
</init-param> 
</filter> 
<filter-mapping> 
<filter-name>encodingFilter</filter-name> 
<url-pattern>*.html</url-pattern> 
</filter-mapping> 
<filter-mapping> 
<filter-name>encodingFilter</filter-name> 
<url-pattern>*.jsp</url-pattern> 
</filter-mapping> 


因為規範要求瀏覽器提交資料都要用utf8編碼,所以這裡設定編碼方式為UTF8. 

特別注意: 
a,這個過濾器只是簡單的呼叫:request.setCharacterEncoding(this.encoding); 
在這個語句之前不能呼叫任何的request.getParameter()方法,否則會設定tomcat的預設字符集為"ISO-8859-1",並且使setCharacterEncoding的呼叫失效.所以在這個過濾器之前的過濾器中不能有對getParameter這類方法的呼叫,比較安全的做法就是把這個過濾器儘量靠前放. 
b,在server.xml中不能加上<Valve className="org.apache.catalina.valves.RequestDumperValve"/> 
這個value也設定tomcat的預設字符集為"ISO-8859-1",使setCharacterEncoding的呼叫失效.可能其他的value也有這個問題,我沒有測試過. 
如果要觀察http請求引數,可以考慮用抓包工具,例如ethereal或者wireshark
3.頁面連結中傳遞中文引數

我虛擬一個這樣的場景,請求頁面中有如下程式碼

Html程式碼

<%   
String username = "編輯";   
%>  
<a href="hello.do?username=<%=username%>">頁面中連結傳遞中文</a>  

對於這種方式,我們需要先將引數使用統一的編碼方式編碼,將編碼後的字元放入連結,這裡我對引數以UTF-8方式編碼,如下

<%   String username = java.net.URLEncoder.encode("編輯","UTF-8");   %>  

那麼這樣我們也不會產生中文亂碼問題

如果是頁面超連線連線中帶的漢字,則編碼根據頁面編碼的不同而不同,如果頁面的 
content="text/html; charset=utf-8",則在tomcat/conf/server.xml中的配置檔案中: 

<!-- Define a non-SSL Coyote HTTP/1.1 Connector on port 8080 --> 
<Connector port="8080" 
maxThreads="150" minSpareThreads="25" maxSpareThreads="75" 
enableLookups="false" redirectPort="8443" acceptCount="100" 
debug="0" connectionTimeout="20000" useBodyEncodingForURI="true" 
disableUploadTimeout="true" /> 


加上:useBodyEncodingForURI="true"即可正常使用getParameter取出正確內容. 
如果content="text/html; charset=GBK",需用 
new String(request.getParameter("something").getBytes("ISO-8859-1"),"GBK") 
取出,其他情況類似

4.     位址列中引數直接輸入中文提交

     從位址列直接輸入漢字,則一般編碼為"GBK",需要用 
new String(request.getParameter("something").getBytes("ISO-8859-1"),"GBK") 取出 
例如瀏覽器位址列中輸入"http://localhost:8080/helloapp.do?username=編輯"提交,對於這種方式,瀏覽器不會採用頁面的charset方式對URL中的中文進行編碼後提交至伺服器(IE,FireFox都一樣),而是採用系統的GBK轉碼為ISO-8859-1之後提交至Servlet容器,那麼,如果對於前三種方式我們所做的設定,在這裡就有問題了,因為進入容器時中文進行了GBK至ISO-8859-1的轉碼,而之前我們的Servlet容器URIEncoding設定為UTF-8,當我們使用request.getParameter("username")時,相當於又進行了這樣的流程GBK-->ISO-8859-1-->UTF-8,按照以上我們使用的測試中文,“編輯”,使用request.getParameter("username")則會得到這樣的結果�༭,下圖是進行轉碼的記憶體快照:

我們可以看到“編輯”經過從GBK-->ISO-8859-1-->UTF-8的過程後得到的就是�༭這樣的結果,這裡我們還會想到那進行2次逆向轉碼看看,不過可惜的是,結果為“錕潔輯”。對於這種情況,我們的解決辦法就是,Tomcat的URIEncoding採用預設的ISO-8859-1字符集,那麼我們可以在程式中通過ISO-8859-1-->GBK這樣不恰當的逆向轉碼方式得到正常的中文“編輯”,但這樣的結果是,我們get請求方式的中文處理解決辦法就需要改變。如,在我的環境下就需要進行ISO-8859-1-->UTF-8的轉碼,挺不爽。

綜上,對於亂碼問題,前三種方式是一般使用者的請求方式,第四種屬於非正常途徑的請求方式,對於這種方式產生的問題我認為無法很好的解決,也不需要解決。我看到javaeye對於這樣的情況就沒有處理,不知道大家在自己的專案中是如何處理的?我的實驗是,IE6的設定會影響應用路徑的編碼方式,例如位址列中請求一箇中文JSP頁面,如:http://localhost:8080/helloapp/編輯.jsp,IE預設是勾選"以UTF-8傳送URL"項的,那麼按照我上面總結的處理方式,這個請求可以正常顯示頁面,如圖:

 

如果取消IE的這個選項,那麼瀏覽器會以GBK編碼應用路徑的中文,得到的結果如圖:
  

按照我上面的設定,這裡如果將Tomcat的URIEncoding設定為GBK,則也可以正常顯示頁面。對於FireFox3.0,則是以UTF-8編碼。
總結
1,所有頁面使用utf8編碼, 
2,伺服器加上過濾器, 
3,server.xml中不要使用 
<Valve className="org.apache.catalina.valves.RequestDumperValve"/> 
4,server.xml檔案加上useBodyEncodingForURI="true" 
這樣應該可以搞定大多數前臺的中文問題.至於位址列輸入中文,不支援也罷,一般的程式很少要求 
從這裡輸入.


相關文章