日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Java平臺(tái)國(guó)際編碼問(wèn)題分析_SUN-JAVA技術(shù)

 linyu2688 2006-11-16



  

                            關(guān)于Java平臺(tái)國(guó)際化數(shù)據(jù)編碼常見(jiàn)問(wèn)題分析   
  王旭科

一、前言

 

(CCW)經(jīng)常在看到有關(guān)討論Java相關(guān)技術(shù)的漢字的顯示,數(shù)據(jù)存儲(chǔ)漢字亂碼的解決方法。本人從事Java平臺(tái)相關(guān)開(kāi)發(fā)也有近5年的時(shí)間,深感這種有關(guān)數(shù)據(jù)編碼(Encoding)的問(wèn)題,不是一個(gè) 簡(jiǎn)單的描述就可以指出問(wèn)題的原因所在。目前零星的解決方案沒(méi)有提供一個(gè)全面的解釋。不同的瀏覽器,Web Server,Application Server,Database,支持的JDK版本不同,及設(shè)計(jì)的架構(gòu)不周全,其組合是一個(gè)龐大的基數(shù),下面就從B/S 3層架構(gòu)的角度,說(shuō)明國(guó)際化的數(shù)據(jù)編碼常見(jiàn)問(wèn)題的分析及解決方法。希望能分享自己的經(jīng)驗(yàn)。

二、常見(jiàn)Java應(yīng)用架構(gòu)

 

以上是基本的Java常見(jiàn)架構(gòu),實(shí)際中可能Web server和應(yīng)用服務(wù)器(Application Server)合二為一,不過(guò)為了清晰起見(jiàn),還是分開(kāi)顯示.

2.1 請(qǐng)求/響應(yīng)(Request/Response) 工作模式

基于B/S架構(gòu)的應(yīng)用的模型,就是終端用戶(hù)通過(guò)瀏覽器向web Server發(fā)送請(qǐng)求,Web解釋并響應(yīng)請(qǐng)求。其事件流模型可以參考標(biāo)準(zhǔn)HTTP協(xié)議獲得細(xì)節(jié)。常見(jiàn)的數(shù)據(jù)轉(zhuǎn)換及傳送發(fā)生以下三個(gè)環(huán)節(jié).所有的轉(zhuǎn)換都是雙向的.負(fù)責(zé)每個(gè)環(huán)節(jié)數(shù)據(jù)編碼(Encoding)的角色不同,需要對(duì)每個(gè)環(huán)節(jié)的角色進(jìn)行正確的參數(shù)設(shè)定互相配合才能得到期望的結(jié)果。

瀏覽器 ?----------?Web Server. Browser Render引擎/Web Server 引擎

WebServer ?-------? 應(yīng)用服務(wù)器 .JSP/Server 引擎及可能存在的其他連接WebServer

和Application Server的Plugin.

應(yīng)用服務(wù)器 ?-------------? 數(shù)據(jù)庫(kù) .每個(gè)數(shù)據(jù)庫(kù)廠家的JDBC驅(qū)動(dòng)器.

這里最重要和最易變的是Browser ?>WebServer ?-?JSP/Servet 之間的數(shù)據(jù)流和事件流發(fā)生的數(shù)據(jù)編碼,JDBC如果只是利用純粹的thin的模式來(lái)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù),邏輯比較簡(jiǎn)單,如果通過(guò)不同數(shù)據(jù)庫(kù)廠家為了提高性能提供的利用本地client的方式來(lái)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù),其配置復(fù)雜性隨不同廠家而不同,這里略過(guò)。以下對(duì)該事件流做詳細(xì)描述,以闡述后面發(fā)生的”內(nèi)幕”.

2.2 瀏覽器 HTML Render引擎如何顯示和提交數(shù)據(jù).核心的URL-Encoding.

瀏覽器渲染html頁(yè)面里的內(nèi)容如何編碼,決策順序如下。

首先瀏覽器根據(jù)Web Server發(fā)送的Content-Type Header,里的Charset信息來(lái)決定自己如何渲染html的顯示。如果沒(méi)有Content-Type,就根據(jù)Html頁(yè)面里的<meta>中的Content-Type來(lái)決定渲染的字符編碼.<meta>一般如下:

<META HTTP-EQUIV="Content-Type"CONTENT="text/html; CHARSET= “UTF-8">

一般出現(xiàn)亂碼的情況,有可能是content-type同實(shí)際數(shù)據(jù)不符,所以使用瀏覽器的”改變

編碼”的功能換一個(gè)字符集合,就能看到正確的數(shù)據(jù).如果以上有關(guān)字符編碼的信息都無(wú)

法得到,瀏覽器采用默認(rèn)的ISO-8859-1來(lái)渲染HTML頁(yè)面

其次,瀏覽器向WebServer通過(guò) Form提交數(shù)據(jù)的時(shí)候,其編碼數(shù)據(jù)的行為決策順序如

1.<Form>屬性的accept-charset,指定的字符編碼

2.<meta>指定的Content-Type

3.url-encoding 默認(rèn)的字符編碼.

這是標(biāo)準(zhǔn)按照HTML Internationalization (參考RFC 2070)規(guī)范的順序。

根據(jù)實(shí)際的經(jīng)驗(yàn),F(xiàn)ireBird 瀏覽器(或Mozilla Familly) 完全順從這個(gè)順序。

但I(xiàn)E 6 是2 <meta>指定的Content-Type優(yōu)先級(jí)別最高,所以,在HTML面在<body>

標(biāo)記之前,<head>標(biāo)記后第一個(gè)寫(xiě)入<meta>來(lái)聲明本頁(yè)的默認(rèn)字符編碼,是良好的習(xí)慣。

可以保證大多數(shù)瀏覽器可以正確渲染HTML數(shù)據(jù)。

如果沒(méi)有指定任何Content-Type,瀏覽器將按照iso-8859-1這種字符編碼.

URL-Encoding的詳細(xì)描述在(RFC 1738),重要的就是URL Encoding Converted.

“Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*‘()," [not including the quotes - ed], and reserved characters used for their reserved purposes may be used unencoded within a URL."”

這種方式下,就是常見(jiàn)的%HH字符串的結(jié)果了。所有字符被編碼成 %HH字符串的方

式被傳遞.

如果<meta>中的charset指明的不符合實(shí)際數(shù)據(jù)或著指定的字符集合不包容實(shí)際輸入的

字符集合,就會(huì)造成編碼錯(cuò)誤,丟失數(shù)據(jù)信息。

總之,瀏覽器向Web Server提交數(shù)據(jù)的時(shí)候,根據(jù)URL-Encodeds 編碼數(shù)據(jù)并且設(shè)置

Content-Type 為application/x-www-form-urlencoded,但沒(méi)有傳送任何有關(guān)charset的信息。

2.2.1 小結(jié)

在HTML頁(yè)面或JSP/Servlet等動(dòng)態(tài)生成的頁(yè)面里,必須指定正確的<meta>信

息,才能保證數(shù)據(jù)顯示渲染和提交給Web Server數(shù)據(jù)是正確編碼的。如果沒(méi)有指定任

何charset的信息,瀏覽器是按照ISO-8859-1編碼顯示和提交數(shù)據(jù),可能造成數(shù)據(jù)信

息的丟失.

2.3 Web Server 如何接受Post/get的數(shù)據(jù)

通過(guò)2.2節(jié)的分析,我們可以知道,默認(rèn)的情況下,WebServer是按照原始數(shù)據(jù)(raw data)來(lái)接受數(shù)據(jù)的。寫(xiě)過(guò)CGI應(yīng)用應(yīng)該知道,這些數(shù)據(jù)是存放在服務(wù)器系統(tǒng)同應(yīng)用相關(guān)的環(huán)境變量Cache中,我們常說(shuō)的context(上下文)就包含了這些原始的提交數(shù)據(jù).

2.4 JSP/Servlet 如何獲取數(shù)據(jù)

當(dāng)使用Servlet調(diào)用getParameter或getParameters時(shí)候,通過(guò)Servlet包容器(Container)上下文來(lái)從WebServer環(huán)境變量中獲取原始數(shù)據(jù)并編碼,但由于沒(méi)有關(guān)于Charset的信息,所以此時(shí)設(shè)置正確的字符編碼,才能把被URL-Encoding的數(shù)據(jù),正確還原。這是通過(guò)設(shè)置request的setCharacterEncoding來(lái)設(shè)置正確的字符集,才能得到正確數(shù)據(jù)。

同時(shí)根據(jù)瀏覽器渲染HTML的規(guī)范,同樣送回瀏覽器的數(shù)據(jù)也必須指定正確的字符集才能保證瀏覽器正確編碼顯示,這是通過(guò)對(duì)response的setContentType方法調(diào)用來(lái)做到的。

在實(shí)際應(yīng)用中,了解了這些原理,不難寫(xiě)出正確的處理數(shù)據(jù)的應(yīng)用。Servlet 2.3規(guī)范提供了Filter技術(shù),可以完美解決Post數(shù)據(jù)和回應(yīng)信息的編碼問(wèn)題,具體例子見(jiàn)附錄1.

如果沒(méi)有Filter技術(shù),則需要使用常用的“重構(gòu)字符串”技術(shù)來(lái)解決這個(gè)問(wèn)題,代碼見(jiàn)附錄2

2.4.1 小結(jié):

判斷在Servlet中是否正確的重構(gòu)了提交的(Post) 數(shù)據(jù),一個(gè)常見(jiàn)的小技巧是通過(guò)System.out.println打印出數(shù)據(jù)到后端控制臺(tái),如果同系統(tǒng)當(dāng)前字符集相同的數(shù)據(jù)能正確顯示,表明重構(gòu)正確,比如你的服務(wù)器是Sun Solaris或Linux ,默認(rèn)語(yǔ)言是中文,那么中文數(shù)據(jù)就可以正確的被打印出來(lái),而不是一堆”?????”.

2.5 JDBC 如何保存數(shù)據(jù)庫(kù)

當(dāng)把通過(guò)2.4 正確重構(gòu)的數(shù)據(jù)要寫(xiě)入數(shù)據(jù)庫(kù)時(shí),同樣要考慮字符編碼的問(wèn)題。

首先必須在執(zhí)行JDBC或使用J2EE CMP通過(guò)setxxxx符值之前,調(diào)整數(shù)據(jù)的編碼同數(shù)據(jù)庫(kù)字符編碼一致,否則可能出錯(cuò)。這種轉(zhuǎn)換同具體使用JDBC Driver 的方式不同而有所不同。假設(shè)純粹使用thin(Type 3或Type 4)的方式,相對(duì)比較簡(jiǎn)單,只要知道正確的數(shù)據(jù)庫(kù)端的字符集,實(shí)現(xiàn)數(shù)據(jù)重構(gòu)為符合數(shù)據(jù)庫(kù)字符編碼的數(shù)據(jù)就可以了,代碼見(jiàn)附錄 2

三、通用國(guó)際化架構(gòu)

1、所有HTML或JSP/Servlet動(dòng)態(tài)頁(yè)面<meta>指明字符集合為UTF-8

2、Servlet 指明設(shè)置request獲取參數(shù)為UTF-8

3、Servlet 指明reponse Content-Type 的charset為UTF-8

4、數(shù)據(jù)庫(kù)編碼指明為UTF-8

這是最簡(jiǎn)明的國(guó)際化框架了.表現(xiàn)層HTML/JSP頁(yè)面可以用最終用戶(hù)本地語(yǔ)言編寫(xiě)保存為Unicode模式,或通過(guò)字典方式根據(jù)用戶(hù)的選擇,來(lái)動(dòng)態(tài)顯示HTML/JSP頁(yè)面上的本地語(yǔ)言提示性標(biāo)簽。在JSTL 1.0推出后,沒(méi)有理由再使用本地語(yǔ)言編寫(xiě)多套HTML/JSP頁(yè)面,帶來(lái)維護(hù)和代碼的復(fù)雜性。當(dāng)然具體應(yīng)用是復(fù)雜的,這里只是給出一個(gè)建議性的措施。

本文給出比較細(xì)節(jié)的解釋了B/S架構(gòu)下涉及到的Java編碼的方面,有助于出現(xiàn)問(wèn)題時(shí),快速定位問(wèn)題環(huán)節(jié)并解決之。

各種規(guī)范發(fā)展的很快,如果本文描述有錯(cuò)誤或更好的實(shí)現(xiàn),歡迎指正.

本文代碼在Jboss 3.2.2 with Tomcat 下調(diào)試通過(guò)。

附錄1:

 

Filter 源代碼:

0 /*

1 * JSPEncoding.java

2 *

3 * Created on 2003年12月17日, 下午9:45

4 */

5

6 package action;

7

8 import java.io.*;

9 import java.net.*;

10 import java.util.*;

11 import java.text.*;

12 import javax.servlet.*;

13 import javax.servlet.http.*;

14

15 import javax.servlet.Filter;

16 import javax.servlet.FilterChain;

17 import javax.servlet.FilterConfig;

18 import javax.servlet.ServletContext;

19 import javax.servlet.ServletException;

20 import javax.servlet.ServletRequest;

21 import javax.servlet.ServletResponse;

22

23 /**

24 *

25 * @author Administrator

26 * @version

27 */

28

29 public class JSPEncoding implements Filter {

30

31 // The filter configuration object we are associated with. If

32 // this value is null, this filter instance is not currently

33 // configured.

34 private FilterConfig filterConfig = null;

35 // default to UTF-8

36 private String targetEncoding = "UTF-8";

37 public JSPEncoding() {

38 }

39

40 private void doBeforeProcessing(ServletRequest request, ServletResponse response)

41 throws IOException, ServletException {

42 if (debug) log("JSPEncoding:DoBeforeProcessing");

43 }

44

45 private void doAfterProcessing(ServletRequest request, ServletResponse response)

46 throws IOExce1ption, ServletException {

47 if (debug) log("JSPEncoding:DoAfterProcessing");

48 }

49

50 /**

51 *

52 * @param request The servlet request we are processing

53 * @param result The servlet response we are creating

54 * @param chain The filter chain we are processing

55 *

56 * @exception IOException if an input/output error occurs

57 * @exception ServletException if a servlet error occurs

58 */

59 public void doFilter(ServletRequest request, ServletResponse response,

60 FilterChain chain)

61 throws IOException, ServletException {

62

63 if (debug) log("JSPEncoding:doFilter()");

64

65 doBeforeProcessing(request, response);

66

67 HttpServletRequest srequest = (HttpServletRequest)request;

68 srequest.setCharacterEncoding(targetEncoding);

69 HttpServletResponse sresponse=(HttpServletResponse)response;

70 sresponse.addHeader("charset", targetEncoding);

71 try {

72 chain.doFilter(srequest, sresponse);

73 }

74 catch(Throwable t) {

75 t.printStackTrace();

76 }

77

78 doAfterProcessing(request, response);

79

80 }

81

82

83 /**

84 * Return the1 filter configuration object for this filter.

85 */

86 public FilterConfig getFilterConfig() {

87 return (this.filterConfig);

88 }

89

90

91 /**

92 * Set the filter configuration object for this filter.

93 *

94 * @param filterConfig The filter configuration object

95 */

96 public void setFilterConfig(FilterConfig filterConfig) {

97

98 this.filterConfig = filterConfig;

99 }

100

101 /**

102 * Destroy method for this filter

103 *

104 */

105 public void destroy() {

106 filterConfig = null;

107 targetEncoding = null;

108 }

109

110

111 /**

112 * Init method for this filter

113 *

114 */

115 public void init(FilterConfig config) {

116

117 this.filterConfig = config;

118 this.targetEncoding = config.getInitParameter("encoding");

119 if (config != null) {

120 if (debug) {

121 log("JSPEncoding:Initializing filter");

122 }

123 }

124 }

125

126 /**

127 * Return a String representation of this object.

128 */

129 public String toString() {

130

131 if (filterConfig == null) return ("JSPEncoding()");

132 StringBuffer sb = new StringBuffer("JSPEncoding(1");

133 sb.append(filterConfig);

134 sb.append(")");

135 return (sb.toString());

136

137 }

138

139

140

141

142

143 public void log(String msg) {

144 filterConfig.getServletContext().log(msg);

145 }

146

147 private static final boolean debug = true;

148 }

附錄2 通用字符串重構(gòu)

/**

*@param pValue is raw data

*@pEncoding is target data Encode

*/

public String convert(String pValue, String pEncoding)

throws IOException

{

byte bytes[] = getBytes(pValue);

return convert(bytes, pEncoding);

byte[] getBytes(String pValue)

{

byte bytes[] = new byte[pValue.length()];

for(int i = 0; i < bytes.length; i++)

bytes[i] = (byte)pValue.charAt(i);

return bytes;

}

public String convert(byte pValue[], String pEncoding)

throws IOException

{

ByteArrayInputStream bais = new ByteArrayInputStream(pValue);

InputStreamReader isr = new InputStreamReader(bais, pEncoding);

StringBuffer sb = new StringBuffer();

for(int c = isr.read(); c != -1; c = isr.read())

sb.append((char)c);

return sb.toString();

}

附錄3

Servlet 2.3 Filter 的在web.xml中的表示

WEB-INF/web.xml

<filter>

<filter-name>action.JSPEncoding</filter-name>

<filter-class>action.JSPEncoding</filter-class>

<init-param>

<param-name>encoding</param-name>

<param-value>UTF-8</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>action.JSPEncoding</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>



    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多