首先简单介绍下,FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,用来生成HTML Web页面,特别是基于MVC模式的应用程序,其作用跟JSP有点类似,不过它不允许在页面中写JAVA代码,所有内容必须提前生成,因此它比JSP能更好地保持界面设计同应用程序逻辑的分离。
今天有同事碰到这个问题,一个FreeMarker的FTL页面代码如下:
<html> ... <body> <#include "/WEB-INF/ftl/AHeader.ftl"> <div class="adlist3">... </div> ... </body> </html>其中在body后紧跟着包含引用了一个公共的导航页头文件,内容如下:
<div class="nn"> <a href="http://xxx.yyy/j/i.action" class="menu">主页</a> ... </div>两个FTL文件都是UTF-8编码。挺简单的一个页面,但执行后发现页头有一点白边,非常奇怪:
IE8、FF、OPERA均有此问题,只有IE9是正常地贴到到顶边。研究了半天源码,也没发现有CSS或空行之类的东西在影响。
进一步测试发现,如果去掉<#include>,直接把内容贴到同一个FTL文件里是不会有问题的;因此可以确认是<#include>导致的问题。
继续调试,发现如果把AHeader.ftl的文件编码修改为GBK,并用<#include "/WEB-INF/ftl/CtWaeHeader.ftl" encoding="GBK">的方式引用,也能避免此问题;因此初步认定问题可能是UTF-8编码引起的。
于是怀疑文件的编码并非真的是UTF-8。用UE打开AHeader.ftl并进入HEX模式,发现居然是UNICODE的:
心想这下找着问题了,连忙用记事本打开,另存为UTF-8,满怀希望地打开浏览器,结果非常失望,问题依旧。
再用UE打开,结果还是UNICODE。原来UE对UTF-8的支持不太好,其实文件可能已经是UTF-8,但被UE打开后自动切换成UNICODE了。
再找到另一个二进制工具,打开AHeader.ftl,发现它确实是UTF-8编码,非常正确的。但这时文件头的前几个BOM字节引起了我的怀疑:
文本编辑器保存UTF-8、UNICODE格式文件时,会在文件头上加一个标记说明它的编码,称为Byte Order Mark,即BOM。
正常情况下,FreeMarker解释器应该要自动识别UTF-8的BOM标记的,但如果它没有识别,直接把BOM输出给浏览器了,就有可能在页面上留下空白了。
于是我尝试用EditPlus去掉BOM。EditPlus有一个保存时不写BOM的选项:
勾上此选项,用EditPlus打开AHeader.ftl再另存回去,这时再用浏览器打开,果然已经没有白边了:
至此问题解决,原因是FreeMarker在引用UTF-8的文件时,没有把文件的BOM处理掉,直接扔给浏览器了。解决办法是在被引用的文件中把BOM标记全去掉。