在做google map api時遇到的字符串編碼的各種糾結

我們的架構是b/s的,後臺用的是tomcat,和google的webservice打交道還用到一個gae做代理。

之前的博文我們說過在tomcat和瀏覽器之間的ajax交互時要用一種客戶端encode兩次加上一個輔助函數加工然後服務端decode兩次的噁心解決方案。後來我們研究了一下tomcat,發現其實問題是這樣的。

 

tomcat默認使用iso-8859-1來解釋request的,所以我們用ff提交參數時用的是utf8,我們在瀏覽器裏輸入“大學”,其實是被ff encode過的(被轉成%B5%33形式),傳到服務器時tomcat又用iso-8859-1 decode了。所以我們想到解決方案如下

	String keyword =URLEncoder.encode(request.getParameter("keyword"),"ISO-8859-1");
		keyword=URLDecoder.decode(keyword, "UTF-8");

 這樣子我們反過來弄一下就ok了。

這裏還要說一下,如果換成時ie的話,默認“大學”這個url參數會用gbk,如果還是用上面的那個,其實就會出現亂碼。這裏還要說glassfish貌似也是用的iso-8859-1

 

這裏還要講一個關於HTTPURLConnection的事情,我們在tomcat用HTTPURLConnection時,我在設置parameter的時候沒有encode我的參數,我就這樣傳過去了,而在本地的jetty服務器的gae上接收到了我的字符串竟然是對的,看來HttpURLConnection會自動幫我們按照默認的charset(linux下是utf8)把字符串編碼再傳過去。然後再我們本地的gae上,可以直接寫這樣的code

     String keyword = req.getParameter("keyword");
        String center = req.getParameter("center");
        String range = req.getParameter("range");

 這個可能gae用的jetty服務器又自動幫我們decode了,這裏雖然我們沒有decode和encode,但是程序都自動幫我們完成了。但是必須滿足客戶端和服務器用的默認的編碼是一樣的。

 

這裏講一下什麼是decode什麼是encode,比如,我們對一個“作者”encode,這裏會encode成     %E4%BD%9C%E8%80%85這樣的,再encode一下會,encode成“%25E4%25BD%259C%25E8%2580%2585”這樣,%會轉成%25。   而decode呢?如果我們對“%25E4%25BD%259C%25E8%2580%2585” decode會變成“%E4%BD%9C%E8%80%85”,而對“%25E4%25BD%259C%25E8%2580%2585大學” decode會變成“%E4%BD%9C%E8%80%85大學”,也就是說decode可能只會對%開頭的東西進行decode。而encode比較詭異,處理%的手段讓人搞不懂。

 

話說回來,我做了幾次實驗,我發現URLConnection會自動幫助我們進行編碼的,而且很智能,如果我們事先encode過,他就不會再幫我們encode。而本地gae會用默認的utf8方式編碼。設想一個情況,我們用gbk encode參數然後再傳到gae上,我們就需要在gae上先encode("UTF-8")在decode 成gbk。這裏的道理是一樣的。而我們可以用一個函數就是request.setCharsetEncoding("GBK")來直接來讓服務器幫我們用GBK轉,這樣就不要先encode再decode了。

 

現在我們看age裏面的代碼,我們需要請求google的web service。這時,我們仍然是需要用HTTPURLConnection這個類,但是我們在沒有encode參數的時候,發給google api時竟然說url是不合法的,一定要實現encode纔可以。這就不懂了,爲什麼一定要我們手動encode呢,不是HTTPURLConnection自動幫我們encode了嗎?我想這是不是同一個版本的URLCOnnection啊?我看了一下原來不加encode的錯誤堆棧信息,原來最後會調用一個什麼httpclient的類,而這個類不是標準庫裏的,是外部的,也不知道這裏面到底出了什麼鬼!!!就連我step into進去HTTPURLConnection,裏面的堆棧也是不一樣的,詭異啊!!!

 

接着我們從webservice裏獲得了json字符串,裏面也還是沒有decode過的utf8,隨即

String allString=URLDecoder.decode(all.toString(),"UTF-8");

 最後還要返回給tomcat結果,此時如果我們不用以下這個字符編碼限定會出問題

        resp.setContentType("text/json");
        resp.setCharacterEncoding("UTF-8");
        PrintWriter writer=resp.getWriter();

 我不知道這個是爲什麼!我看本地的age上默認是utf8啊,難道傳過來的時候就不是utf8?

後來我又搞成這樣子,在本地gae上這樣寫

		OutputStream writer = resp.getOutputStream();
		writer.write(jsonString.getBytes());
		writer.close();

 在tomcat上這樣寫

		byte[] bytes=new byte[10000];
		InputStream inputStream=connection.getInputStream();
		inputStream.read(bytes);
		String string=new String(bytes);

 這樣我們用純字節進行傳遞就不可能會有錯了!!!這裏我沒有指定到底用什麼編碼傳遞,這個時候會用默認的,我始linux的,所以java的默認編碼用utf8。

 

 

我們把age部署到服務器,在服務器上出現了詭異,原來服務器上用的是一個us什麼的默認編碼格式。真正的age要想正常返回給我們字符串,需要加上這麼一句話

	BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"));
				StringBuilder all = new StringBuilder();
				String line = null;
				while ((line = reader.readLine()) != null) {
					all.append(line);
				}
				
 

 在我本地gae是不需要加第一句話中的UTF-8的。因爲在服務器上的gae是us編碼的,所以需要這麼轉,否則默認就轉成us這種東西了。

這裏就又有一個問題了,service傳過來的是一個需要decode的東西,我在本地的gae上用

new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"));

得到的字符串是需要再一次decode的,而在服務器上的gae是不需要再一次被decode的。

 

終其所述,無論本地還是服務器的age下的HTTPURLConnection和普通java的不一樣,它不會幫我們encode;而服務器上gae的InputStreamReader和本地的gae和普通java也不一樣,他會自動轉碼。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章