You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

设置Freemarker默认编码为UTF-8为何导致模板读取异常?

问题背景

基于Gradle KTS的Spring Boot项目,使用独立Freemarker库implementation("org.freemarker:freemarker:2.3.32")生成文本模板,核心代码如下:

Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
cfg.setDirectoryForTemplateLoading(new ClassPathResource("templates").getFile());
cfg.setDefaultEncoding("utf-8");
cfg.setOutputEncoding("utf-8");

Template template = cfg.getTemplate("template.ftl");
System.out.println("template=" + template);

Map<String, Object> data = new HashMap<>();
data.put("vehicles", List.of(new Vehicle("auto", 60), new Vehicle("koloběžka", 5)));

StringWriter stringWriter = new StringWriter();
template.process(data, stringWriter);
String result = stringWriter.toString();

System.out.println(result);

测试用模板文件template.ftl内容为:

jůůů

模板文件和传入数据均包含捷克字符,且IntelliJ显示模板为UTF-8编码。

异常现象

  • 不设置Configuration的默认编码时,Freemarker使用本地编码(推测为Windows-1250),模板能正确打印含变音符号的内容;
  • 显式设置默认编码为UTF-8后,打印结果变为乱码j���。

疑问

  1. 为何模板与配置均为UTF-8时无法正确读取变音符号?
  2. 为何默认Windows-1250编码能正常读取UTF-8编码的模板?

原因分析

1. 无编码配置时正常的巧合

捷克字符ů在UTF-8编码中是两个字节C5 AF,而这两个字节恰好与Windows-1250编码中ů的字节值完全一致。因此当Freemarker用Windows-1250读取UTF-8编码的模板文件时,刚好能解析出正确的字符,这属于编码字节重叠的巧合,并非正常兼容逻辑。

2. 设置UTF-8后乱码的核心原因

问题出在Gradle构建过程的资源处理和模板加载方式:

  • Gradle默认会以系统编码(Windows下为Windows-1250)复制src/main/resources下的资源文件。即使你在IntelliJ中把模板设为UTF-8,Gradle构建时会将其转码为Windows-1250后输出到构建目录;
  • 此时用UTF-8编码读取转码后的文件,自然会出现乱码。而无编码配置时用Windows-1250读取转码后的文件,刚好匹配,所以显示正常。
  • 另外,ClassPathResource("templates").getFile()的加载方式存在隐患:当项目打包为JAR后,类路径内的资源无法通过File对象直接访问,会抛出异常。

解决方法

方法一:强制Gradle以UTF-8处理模板资源

在build.gradle.kts中添加配置,确保Freemarker模板文件以UTF-8编码复制,避免转码:

tasks.withType<ProcessResources> {
    filteringCharset = "UTF-8"
    filesMatching("**/*.ftl") {
        options.encoding = "UTF-8"
    }
}

方法二:改用类路径加载器读取模板(推荐)

放弃File方式加载,改用Freemarker的ClassTemplateLoader直接从类路径读取资源,这样既避免JAR打包后的访问问题,也能确保编码读取正确:

Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
// 从当前类的类路径下加载/templates目录中的模板
cfg.setTemplateLoader(new ClassTemplateLoader(getClass(), "/templates"));
cfg.setDefaultEncoding("utf-8");
cfg.setOutputEncoding("utf-8");

内容的提问来源于stack exchange,提问作者milkamar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 13:15:56