看了《深入分析java web技術內幕》,感覺編碼這部分寫的挺有總結性的,自己總結了書上的內容,記錄下一些知識點,希望能有更多朋友受益O(∩_∩)O
1.哪些操作中會存在編碼?
1.1)I/O操作中存在的編碼:在I/O中,字符與字節之間的相互轉換 的操作中,通常用到InputStreamReader與OutputStreamWriter來 實現,要注意使用統一的編解碼字符集,一般就不會出現亂碼問 題
1.2)內存操作中存在的編碼:
字符串—>字節:String對象.getBytes(String charset);
字節—>字符串:new String(byte[] b,String charset);
2.使用Charset類進行編碼與解碼:
Charset charset = Charset.forName(String charset);
編碼:ByteBuffer byteBuffer = charset.encode(String s);
解碼:CharBuffer charBuffer = charset.decode(ByteBuffer b);
ByteBuffer類提供一個緩衝區實現對字符與字節的軟件換,無需編碼與解碼,實際值並沒修改,僅僅類型做了轉換,把16bit的 char變成兩個8bit的byte
ByteBuffer byteBuffer = ByteBuffer.allocate(int bufferSize);
//初始化分配一個緩衝區
byteBuffer.putChar(Char char); //字符—>字節
byteBuffer.getChar(int index); //字節—>字符
3.Java中如何編解碼:
3.1)根據指定的字符集通過Charset.forName(String charset)創建 Charset類;
3.2)根據Charser類創建CharsetEncoder對象;
3.3)調用CharsetEncoder.encode(String s)進行編碼
4.Java web中哪些地方可能存在編碼轉換:
4.1)發起HTTP請求時:URL、Cookie、請求參數
4.2)服務端響應時:讀取的數據庫數據、本地或網絡中的文本文件
4.3)URL上的編解碼:
在URL組成中,可能出現中文的部分有Path Info與Query String 這兩部分,且瀏覽器對這兩部分採取不同的編碼,不同瀏覽器對 Path Info部分的編碼也不一樣;Query String部分的解碼字符集是 通過HTTP的Header傳到服務器端的,其字符集要麼是通過 Header中的ContentType定義的charset,要麼就是默認的 ISO-8859-1,要是有Header中定義的需要做一些相應的配置
在tomcat中,對URL中的URI部分進行解碼的字符集是在connector的<Connector URIEncoding=”UTF-8” />中定義的,若沒定義則使用ISO-8859-1解析,故有中文URL時最好把URIEncoding設爲UTF-8;獲取Query String部分是通過request.getParameter方法實現的,之後進行解碼;對於Query String部分要使用ContentType中定義的編碼就需要在connector的<Connector URIEncoding=”UTF-8” useBodyEncodingForURI=”true” />中的useBodyEncodingForURI設置爲true,該設置僅僅是對Query String部分使用BodyEncoding編碼,故在服務器端最好設置<Connector/>中的URIEncoding與useBodyEncodingForURI這兩個參數
4.4)HTTP Header的編解碼:
在tomcat中,對Header中的項進行解碼是在調用 request.getHeader 方法時進行的,若請求的Header項沒有解碼 則調用MessageBytes 的toString方法,該方法從byte到char 的轉換使用的默認編碼是 ISO-8859-1,若設置的Header中有 非ASCII字符解碼肯定會有亂 碼,所以儘量不要在Header中 傳遞非ASCII字符,若必需傳遞,先用 org.apache.catalina.util.URLEncoding編碼後添加到Header 中,屆時再用對應的字符集解碼即可
4.5)POST表單的編解碼:
POST表單進行解碼是在第一次調用request.getParameter方法 時發生的;當提交POST表單時,瀏覽器先根據ContentType 的Charset編碼格式對錶單內容進行編碼,後提交到服務器端, 在服務器端也是用ContentType的Charset進行解碼的,故表 單提交一般不會出現亂碼,但一定要在第一次調用 request.getParameter之 前就設置 request.setCharacterEncoding(charset),否則也可能有亂 碼,若 沒設置則使用默認的ISO-8859-1編碼;對於 multipart/form-data類型參數,同樣使用Content-Type定義的字 符集編碼,上傳文件使用字節流方式傳輸到服務器的本地目 錄,此過程不涉及字符編碼,在將文件內容添加到parameters 時才發生編碼,如果用這個不能編碼則採用默認的 ISO-8859-1編碼
4.6)HTTP BODY的編解碼:
服務端響應客戶端的請求返回資源這一過程,要先編碼再瀏覽 器解碼,可通過response.setCharacterEncoding設置,該設置會 覆蓋request.setCharacterEncoding的值,並通過Content-Type 返回瀏覽 器,瀏覽器接收時用Content-Type進行解碼;若 Header中的Content-Type沒有設置,則根據HTML中的<meta http-equiv=”Context-Type” content=”text/html; charset=UTF-8” />中的charser進行解碼,HTML中的再無設置則使用默認的編 碼來解碼
4.7)對於數據庫編碼問題,可以通過設置JDBC的URL來指定
如: url=”jdbc:mysql://localhost:3306/DB?useUnicode=true&characte rEn coding=UTF-8”
4.8)JS中的編碼問題:
外部引入js文件:
<script src=” ” charset=”UTF-8”></script>
引入一個包含中文的js文件,如果script標籤上沒有設 置 charset,則瀏覽器以當前頁面默認的字符集解析該js文件;若 外部js文件與當前頁面的編碼格式一致,則可以不用設置 charset;若不設置charset且js文件編碼格式與當前頁面不一 致,則亂碼
4.9)JS的URL編碼:
escape(String s)函數:將ASCII字母、數字、標點符號之外的 其他字符轉換成Unicode編碼值,並在其編碼值前加“%u”; 解碼用unescape(String s)函數,可以防止信息丟失
(上述兩個方法一般用encodeURL、encodeURLComponent代 替)
encodeURL(String s)函數:可以將整個URL中的字符(除特殊 字符外)進行UTF-8編碼,在每個碼值前加“%”;解碼使用 decodeURL(String s)函數
encodeURLComponent()函數:除了對 ‘ ( ) * - . _ ~ 0-9 a-z A-Z 不編碼外,其餘都編碼,通常用於將一個URL當做一個參數 放在另一個URL中;解碼通過decodeURLComponent(String s) 函數
4.10)java與js編碼問題:
Java端處理URL編解碼有兩個類,java.net.URLEncoder和 java.net.URLDecoder,分別對應前端js的 encodeURLComponent 與decodeURLComponent;在前端使 用encodeURLComponent編 碼後,到服務端使用URLDecoder 解碼可能會發生亂碼,原因是 前端js的默認編碼是UTF-8, 而後端對於中文的編碼一般是GBK 或GB2312,用GBK去 解碼UTF-8的編碼,必定發生亂碼;解決方法:用 encodeURLComponent去編碼2次,即 encodeURLComponent(encodeURLComponent(String s)),這樣 在使用request.getParameter用GBK解碼後取得的就是UTF-8 編碼的字符串,若在java端要使用該字符串,再用UTF-8解 碼就可以;若是這個結果直接通過js輸出到前端,則該UTF-8 字符串輸出可以正常顯示
4.11)其他需要編碼的地方:
除了URL和參數編碼的問題外,XML、Velocity、JSP等也可 能存在編碼
XML:<?xml version=”1.0” encoding=”UTF-8”?>
Velocity:services.VelocityService.input.encoding=UTF-8
JSP:<% @page contentType=”text/html; charset=UTF-8”%>
PS:以上圖片摘自《深入java web 技術內幕》