最近項目中出現了一個問題,圖片的路徑正確,但是轉成URL之后無法找到。。。
找了各種原因之后,最后注意到URL中的圖片名稱和本地路徑名稱有點不一樣,如下圖
1.URL圖片
2.本地路徑
上網查了一下發現加號轉URL應該是轉成“%2B” ,而“%20”代表的是空格,再回頭找到數據庫,果然發現“+”都被替換成“ ”了,如圖
3.數據庫路徑
對應的URL路徑中“%20”改回“+”對應編碼“%2B”之后,也終於可以正常查看圖片了,
4.URL路徑將“%20”改回“+”
以下是網上找到的一片大神的文章,在此摘錄,以防遺失:
鏈接:http://www.verydemo.com/demo_c128_i96717.html
url中帶有加號的處理方法
問題起因:
客戶訂購了一關鍵字為"e+h變送器" , 在首頁推薦廣告中,會根據用戶在search搜索過的關鍵字進行一個匹配投放。技術實現是UED通過JS獲取cookie中的h_keys內容,拼裝到 http://xxxxx/advert/ctp_advert.htm?num=4&keyword={keyword}。 這里取出來對應的cookie信息為中文,最后通過一個ajax發起一個GET請求。
所以針對最后的請求是:http://xxxxxx/advert/ctp_advert.htm?num=4&keyword=e+h變送器。 而在服務端接受到對應的請求參數時,發現參數為:e h變送器, +號沒了。 初步懷疑跟URL規范相關,需要進行url encode。
問題分析:
查了下JS encode的相關內容, 總於發現+號的秘密。
html中因為一些非標准的做法,將+等同於空格進行處理(當Html的表單被提交時, 每個表單域都會被Url編碼之后才在被發送。由於歷史的原因,表單使用的Url編碼實現並不符合最新的標准。例如對於空格使用 的編碼並不是%20,而是+號,如果表單使用的是Post方法提交的,我們可以在HTTP頭中看到有一個Content-Type的header,值為 application/x-www-form-urlencoded,大部分應用程序均能處理這種非標准實現的Url編碼)。
在搜索引擎中做了下嘗試:
keyword = e h變送器 , url =http: //www.google.cn/search?hl=zh-CN&newwindow=1&q=e+h變送器(空格被轉化為+號)
keyword = e+ h變送器 , url = http: //www.google.cn/search?hl=zh-CN&newwindow=1&q=e%2Bh變送器 (+號被進行了轉義為%2B,程序才能正常處理)
問題解決:
思路1:
1. 要想正常傳輸+號而不被轉義為空格,需要進行進行編碼為%2B。查了下幾個編碼函數,發現只有encodeURIComponent 才會對+號進行編碼處理。
2. encodeURIComponent默認為采用UTF-8字符集,理論上只需要在原先的請求中添加_input_charset=utf-8(由 pipeline中的SetLocaleValve進行解析),就可以得到正確的 e+h變送器。
在實施過程中,發現結果並不是預期的那樣。 客戶端通過js encode后,在服務端解析后一直是亂碼。 查了下byte,發現服務端一直是用GBK在進行解析, 針對變送器的UTF-8編碼的byte為{-27,-113,-104,-23,-128,-127},客戶端用GBK解析后變為{-27.-113.- 104.-23,-63,-63},針對最后兩byte因為字符不可見,導致全部被替換為-63。網上查了下,針對 utf-8 -> gbk -> utf-8 在一定情況下就會出現該問題(http://lingqi1818.iteye.com/blog/348953)。
思路2:
繼續追查對應的_input_charset=utf-8未生效的原因,DEBUG看到在SetLocaleValve中的確設置了request.setCharsetEncoding為utf-8。初步懷疑是否跟jboss server的配置有關,查了下跟URIEncoding 和useBodyEncodingForURI 設置有關。 目前公司所使用的jboss為4.05,對應俄tomact配置中只指定了對應的URIEncoding=GBK。正因為這樣,導致設置的_input_charset針對GBK的提交沒有效果,還是按照GBK進行解析。
1. 考慮將請求由GET換成POST , 這樣就可以使用_input_charset
但在實施過程中,和UED溝通過程,針對POST的會引起一個跨域請求的問題。此方案又只能做罷
思路3 (實踐成功):
1. UED進行偽url encode的實現 , 將+號進行%2B的編碼。 因為目前JS中沒有現成的函數,這里只是通過replace(/\+/g, '%2B') 進行了轉化。
總 結
針對+號的處理,針對不同的業務場景需要不同的處理方案,描述下幾種場景:
1. 非Ajax 請求
可以直接使用Form表單的 GET ,POST的urlencode協議,自動實現+ => %2B 的轉化
2. Ajax 請求
* GET請求 : 很無奈,只能使用方案3,人為進行+號轉化。
* POST請求(同一應用,非跨域請 求) : 使用encodeURIComponent + _input_charset=utf-8 指定編碼進行處理。
ps: 前面提的這幾種方案,都是基於+號是正常的業務場景進行考慮。同時我們也可以從業務層面進行一個梳理,+號處理是否有其必要性,能從業務數據入口直接規避 那就最好了。
背景知識:
URIEncoding和useBodyEncodingForURI
對於URL提交的數據和表單中GET方式提交的數據,在接收數據的JSP中設置request.setCharacterEncoding參數是不行的, 因為在Tomcat5.0中,默認情況下使用ISO- 8859-1對URL提交的數據和表單中GET方式提交的數據進行重新編碼(解碼),而不使用該參數對URL提交的數據和表單中GET方式提交的數據進行 重新編碼(解碼)。要解決該問題,應該在Tomcat的配置文件的Connector標簽中設置useBodyEncodingForURI或者 URIEncoding屬性,其中useBodyEncodingForURI參數表示是否用request.setCharacterEncoding 參數對URL提交的數據和表單中GET方式提交的數據進行重新編碼,在默認情 況下,該參數為false(Tomcat4.0中該參數默認為true); URIEncoding參數 指定對所有GET方式請求(包括URL提交的數據和表單中GET方式提交的數據)進行統一的重新編碼(解碼)的編碼。URIEncoding和useBodyEncodingForURI區別是,URIEncoding是對所有GET方式的請求的數據進行統一的重新編碼 (解碼),而useBodyEncodingForURI則是根據響應該請求的頁面的request.setCharacterEncoding參數對數 據進行的重新編碼(解碼),不同的頁面可以有不同的重新編碼(解碼)的編碼。所以對於URL提交的數據和表單中GET方式提交的數據,可以修改 URIEncoding參數為瀏覽器編碼或者修改useBodyEncodingForURI為true,並且在獲得數據的JSP頁面中 request.setCharacterEncoding參數設置成瀏覽器編碼。
為什么需要Url編碼
1. Url中有些字符會引起歧義 , =,&號等
2. Url的編碼格式采用的是ASCII碼,而不是Unicode,這也就是說你不能在Url中包含任何非ASCII字符,例如中文
哪些字符需要編碼
RFC3986文檔規定,Url中只允許包含英文字母(a-zA-Z)、數字(0-9)、-_.~4個特殊字符以及所有保留字符。
Url可以划分成若干個組件,協議、主機、路徑等。RFC3986中指定了以下字符為保留字符: ! * ' ( ) ; : @ & = + $ , / ? # [ ]
如何對Url中的非法字符進行編碼
Url編碼通常也被稱為百分號編碼(Url Encoding,also known as percent-encoding),是因為它的編碼方式非常簡單,使用%百分號加上兩位的字符——0123456789ABCDEF——代表一個字節的 十六進制形式。Url編碼默認使用的字符集是US-ASCII。例如a在US-ASCII碼中對應的字節是0x61,那么Url編碼之后得到的就是%61,我們在地址欄上輸入http: //g.cn/search?q=%61%62%63,實際上就等同於在google上搜索abc了。又如@符號在ASCII字符集中對應的字節為0x40,經過Url編碼之后得到的是%40。
Javascript中的escape,encodeURI和encodeURIComponent的區別
Javascript中提供了3對函數用來對Url編碼以得到合法的Url,它們分別是escape / unescape,encodeURI / decodeURI和encodeURIComponent / decodeURIComponent。解碼和編碼的過程是可逆的.
兼容性不同
escape函數是從Javascript1.0的時候就存在了,其他兩個函數是在Javascript1.5才引入的。但是由於Javascript1.5已經非常普及了,所以實際上使用encodeURI和encodeURIComponent並不會有什么兼容性問題。
對Unicode字符的編碼方式不同
這三個函數對於ASCII字符的編碼方式相同,均是使用百分號+兩位十六進制字符來表示。但是對於Unicode字符,escape的編碼方式是% uxxxx,其中的xxxx是用來表示unicode字符的4位十六進制字符。這種方式已經被W3C廢棄了。但是在ECMA-262標准中仍然保留着 escape的這種編碼語法。encodeURI和encodeURIComponent則使用UTF-8對非ASCII字符進行編碼,然后再進行百分號 編碼。這是RFC推薦的。因此建議盡可能的使用這兩個函數替代escape進行編碼。
適用場合不同
encodeURI被用作對一個完整的URI進行編碼,而encodeURIComponent被用作對URI的一個組件進行編碼。
安全字符不同
可到這里查看具體方法的使用:http://www.w3school.com.cn/js/jsref_encodeURIComponent.asp
escape(69個) */@+-._0-9a-zA-Z
encodeURI(82個)!#$&'()*+,/:;=?@-._~0-9a-zA-Z
encodeURIComponent(71個)!'()*-._~0-9a-zA-Z (注意+ 號未在其安全字符里)
其他和Url編碼相關的問題
對於包含中文的Url的處理問題,不同瀏覽器有不同的表現。例如對於IE,如果你勾選了高級設置“總是以UTF-8發送Url”,那么Url中的路徑部分 的中文會使用UTF-8進行Url編碼之后發送給服務端,而查詢參數中的中文部分使用系統默認字符集進行Url編碼。為了保證最大互操作性,建議所有放到 Url中的組件全部顯式指定某個字符集進行Url編碼,而不依賴於瀏覽器的默認實現。
另外,很多HTTP監視工具或者瀏覽器地址欄等在顯示Url的時候會自動將Url進行一次解碼(使用UTF-8字符集),這就是為什么當你在 Firefox中訪問Google搜索中文的時候,地址欄顯示的Url包含中文的緣故。但實際上發送給服務端的原始Url還是經過編碼的。你可以在地址欄 上使用Javascript訪問location.href就可以看出來了。在研究Url編解碼的時候千萬別被這些假象給迷惑了。