轉自: http://cnxiaowei.javaeye.com/blog/262766
今天要發布一個N久以前做過的項目, 要用Resin(題外話:現在基本已經拋棄Resin, 用Tomcat為主了)遇到了同樣的問題, 原因就是這篇文章裡面描述的UTF-8 BOM問題.
一下是原文:
===================================================================================
以前一直使用resin-3.0.x作為伺服器,最近想升級到resin3.1,於是在官網上下載了resin3.1.7a,解壓配置一切正常,但把應用重新部署上去的時候就出了問題,以前一直正常的頁面,現在卻報錯:
500 Servlet Exception
<script type="text/javascript"></script>[show] /index.jsp:1: contentType 'text/vnd.wap.wml; charset=utf-8' conflicts withprevious value of contentType 'text/html; charset=UTF-8'. Check the .jspand any included .jsp files for conflicts.1: <%@page contentType="text/vnd.wap.wml; charset=utf-8"%>2: <%@page import="java.util.*"%>3: <%!
根據上面的提示,意思似乎是我在jsp裡面第一行設定的contentType是'text/vnd.wap.wml; charset=utf-8,和前面設定的'text/html; charset=UTF-8'不同導致衝突,但這個檔案的第一行就是<%@page contentType="text/vnd.wap.wml;charset=utf-8" %>,根本沒有設定過'text/html; charset=UTF-8',這個提示真是讓人很摸不著頭腦。
後來想到可能是UTF8檔案格式的問題,就用UE開啟檔案,另存了一次,選的是不帶BOM的UTF8格式的檔案,這次就可以正常顯示了。但伺服器上那麼多檔案,不可能一個一個的改,還得想其他的辦法解決。在網上找了很久都沒有任何頭緒,似乎遇到這個問題的人很少。
最後實在是沒辦法,只好把原始碼下載下來研究一下看了,還真的找出了原因所在。
因為resin在處理jsp檔案的時候,會首先讀取前面的幾個位元組來判斷檔案的格式,如果第一個位元組是0xef、第二個位元組是0xbb、第三個位元組是0xbf,那麼就認為這個檔案是UTF8格式,於是就自作主張的把ContentType設定成了text/html; charset=UTF-8,然後在後面的處理過程中,因為jsp程式裡面會有設定ContentType的指令,遇到這個指令會發現和之前的text/html; charset=UTF-8不同,因此就拋出了異常。而如果沒有BOM格式的UTF8,前面就不會有那三個位元組的標識,所以就不會被處理了。
相關代碼:
Java代碼
- case 0xef:
- if ((ch = stream.read()) != 0xbb) {
- stream.unread();
- stream.unread();
- }
- else if ((ch = stream.read()) != 0xbf) {
- throw error(L.l("Expected 0xbf in UTF-8 header. UTF-8 pages with the initial byte 0xbb expect 0xbf immediately following. The 0xbb 0xbf sequence is used by some application to suggest UTF-8 encoding without a directive."));
- }
- else {
- _parseState.setContentType("text/html; charset=UTF-8");
- _parseState.setPageEncoding("UTF-8");
- stream.setEncoding("UTF-8");
- }
- break;
判斷衝突的代碼:
Java代碼
- else if (CONTENT_TYPE.equals(name)) {
- String oldContentType = _parseState.getContentType();
-
- if (oldContentType != null && ! value.equals(oldContentType))
- throw error(L.l("contentType '{0}' conflicts with previous value of contentType '{1}'. Check the .jsp and any included .jsp files for conflicts.", value, oldContentType));
-
- _parseState.setContentType(value);
- String charEncoding = parseCharEncoding(value);
- if (charEncoding != null)
- parseState.setCharEncoding(charEncoding);
- }
真不明白為什麼resin要這麼做呢,如果是web網站可能影響不大,contentType本來就是text/html,但如果是wap或者其他contentType的網站這麼“智能”的編碼判斷方式問題就比較麻煩了。
附:UTF-8 編碼的檔案可以分為no BOM 和 BOM兩種格式(轉載)
何謂BOM? "EF BB BF" 這三個位元組就叫BOM,BOM的全稱叫做"Byte Order Mard".在utf-8檔案中常用BOM來表明這個檔案是UTF-8檔案,而BOM的本意實在utf16中用來表示高低位元組序列的。
在位元組流之前有BOM表示採用低位元組序列(低位元組在前面),而utf8不用考慮位元組序列,所以其實有無BOM都可以。
微軟的記事本 Word 等只能正確開啟含BOM的UTF8檔案,然後UltraEdit卻恰恰相反,回把BOMutf8檔案 誤認為ascii編碼。
UTF-8的BOM是 EFBBBF,因為UE載入UTF-8檔案會轉成Utf16,上述的EFBBBF 在Utf16中是FFFE(Unicode-LE的BOM),UltraEdit不認識BOM又加多一個BOM,所以有2個FFFE。
檔案就被它破壞了。