Maven构建报错Input length = 1?资源文件编码问题排查与修复指南
1. 这个报错到底在说什么从Input length 1读懂Maven的沉默抗议第一次看到Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources后面跟着一句莫名其妙的Input length 1我盯着屏幕愣了大概半分钟。这行日志的诡异之处在于它既没有告诉你哪个文件出了问题也没有堆栈信息指向具体代码行就干巴巴甩出一句输入长度为1然后构建就挂了。如果你正在赶进度这种报错绝对能让人血压飙升。先把结论摆在前面这个报错99%的情况是资源文件编码问题导致的具体来说是maven-resources-plugin在读取某个资源文件时遇到了它无法用当前配置的字符集解析的字节序列。Input length 1是 Java 的CharsetDecoder在解码时抛出的MalformedInputException的经典消息意思是我拿到了一段长度为1的输入但它不符合我当前字符集的编码规则我解不了。为什么这个报错这么难排查因为 Maven 在资源处理阶段是批量扫描src/main/resources和src/test/resources目录的它不会告诉你具体是哪个文件触发了异常。你只能靠二分法或者编码检测来定位。而且这个报错在不同 Maven 版本、不同 JDK 版本下的表现还不完全一样有时候会带上文件名有时候就是光秃秃一句话。这篇文章我会把这个问题彻底拆开讲清楚从maven-resources-plugin的工作机制到Input length 1的底层触发原理再到完整的排查链路和多种修复方案。不管你是刚接触 Maven 的新手还是被这个报错折磨过的老手都能从里面找到可以直接抄作业的步骤。提示如果你现在正卡在这个报错上先别急着改pom.xml的编码配置。很多时候问题不在配置本身而在于某个资源文件里混入了脏字节。盲目改配置可能掩盖问题后面还会以更诡异的方式爆出来。2. maven-resources-plugin 在构建流程里到底干了什么2.1 资源处理阶段的位置与职责要理解这个报错得先搞清楚maven-resources-plugin在整个 Maven 生命周期里扮演什么角色。Maven 的默认生命周期里process-resources阶段绑定的是resources:resources目标process-test-resources阶段绑定的是resources:testResources。这两个目标做的事情很朴素把src/main/resources和src/test/resources目录下的文件复制到target/classes和target/test-classes目录里。听起来很简单对吧但复制这个动作在 Maven 里不是简单的文件拷贝。它涉及到几个关键处理变量替换filtering如果开启了 filteringMaven 会把资源文件里的${...}占位符替换成pom.xml里定义的属性值。这个过程需要把文件内容读成字符串替换后再写回去。编码转换读取文件时用一套字符集写入时用另一套字符集。如果两边不一致或者源文件的字节序列不符合声明的字符集就会抛异常。文件过滤根据includes和excludes配置决定哪些文件需要处理哪些直接跳过。Input length 1这个报错就发生在读取文件这一步。maven-resources-plugin底层用的是 Plexus Utils 的FileUtils.fileRead()或者类似的 IO 工具这些工具在读取文件时会用InputStreamReader配合指定的Charset来解码字节流。当字节流里出现了不符合该字符集规则的字节序列时CharsetDecoder就会抛出MalformedInputException而Input length 1正是这个异常的消息内容。2.2 为什么偏偏是length 1这里有个很多人困惑的点为什么是长度为1难道文件里只有一个字符有问题其实Input length 1里的1不是指文件里只有一个坏字节而是指CharsetDecoder在解码过程中当前这一步拿到的输入缓冲区里从当前位置开始有1个字节无法被解码。换句话说解码器是逐段处理字节的当它发现某个位置的字节序列不符合规则时就会报告我这里拿到了长度为1的输入但它不合法。举个具体的例子。假设你的文件是 UTF-8 编码的但pom.xml里配置的project.build.sourceEncoding是GBK。UTF-8 的中文字符通常是3个字节比如中是E4 B8 AD。当解码器用 GBK 去解这3个字节时GBK 是双字节编码它会把E4 B8当成一个字符剩下AD单独一个字节。这个AD在 GBK 里可能不是一个合法的首字节解码器就会报Input length 1。反过来也一样。如果文件是 GBK 编码配置是 UTF-8GBK 的中文字符是2个字节UTF-8 解码器看到这两个字节时可能第一个字节是合法的 UTF-8 首字节但第二个字节不符合后续字节的规则同样会报错。还有一种常见情况文件本身是 UTF-8 无 BOM 的但里面混入了从 Windows 记事本保存时带上的 BOM 头EF BB BF或者从某些系统复制粘贴时带入了不可见的控制字符。这些脏字节在特定编码下就会触发解码异常。2.3 报错信息为什么经常缺胳膊少腿正常情况下MalformedInputException应该带上出错的位置信息但maven-resources-plugin在捕获异常后往往只把异常消息原样往上抛没有附加文件名和行号。这就导致你看到的日志只有一句Input length 1完全不知道是哪个文件惹的祸。更麻烦的是Maven 的日志级别和插件版本会影响报错的详细程度。有些版本的maven-resources-plugin会在报错前打印正在处理的文件名有些版本则什么都不打。如果你用的是 3.2.0 这个版本恰好它的日志输出比较吝啬排查起来就更费劲。注意不要指望通过加-X参数就能直接看到出错文件名。-X会输出大量调试信息但资源处理阶段的文件级日志往往还是不够明确。真正有效的定位手段是下面要讲的二分排查法和编码检测法。3. 定位问题文件一套可复现的排查链路3.1 第一步确认报错发生在哪个资源目录在动手排查之前先缩小范围。maven-resources-plugin会处理两个目录src/main/resources和src/test/resources。报错可能发生在任意一个。你可以先执行一次只处理主资源的构建mvn process-resources -X 21 | grep -A 5 maven-resources-plugin如果报错出现在这一步问题就在src/main/resources。如果这一步通过了再执行mvn process-test-resources -X 21 | grep -A 5 maven-resources-plugin这样就能确定是主资源还是测试资源出了问题。别小看这一步很多人一上来就全局搜索结果在错误的方向上浪费了大量时间。3.2 第二步用二分法锁定具体文件确定目录后接下来要找出具体是哪个文件。最笨但最有效的方法是二分法把src/main/resources下的文件分成两半暂时把其中一半移到临时目录。执行mvn process-resources看是否还报错。如果还报错说明问题在剩下的一半里如果不报错了说明问题在被移走的那一半里。对有问题的那一半重复上述步骤直到锁定单个文件。这个方法听起来很土但在没有明确日志的情况下它是最高效的。一个中等规模的项目资源文件通常也就几十到几百个二分法最多七八轮就能定位到具体文件。如果你嫌手动移动文件太麻烦可以写个简单的脚本辅助#!/bin/bash # 二分排查脚本示例 RESOURCE_DIRsrc/main/resources TEMP_DIR/tmp/resource_backup # 备份所有资源 cp -r $RESOURCE_DIR $TEMP_DIR # 这里需要你手动配合每次移动一半文件后运行 mvn process-resources # 根据结果决定保留哪一半当然更省事的办法是用下面的编码检测法直接扫描。3.3 第三步用 file 命令和 iconv 快速检测编码在 Linux 或 macOS 上file命令可以快速查看文件的编码信息file -i src/main/resources/*.properties file -i src/main/resources/*.xml file -i src/main/resources/**/*.jsonfile -i会输出类似charsetutf-8或charsetiso-8859-1的结果。如果你发现某个文件显示的不是utf-8而你的项目配置是 UTF-8那它很可能就是罪魁祸首。更精确的检测可以用iconv尝试转换iconv -f UTF-8 -t UTF-8 src/main/resources/config.properties -o /dev/null如果文件不是合法的 UTF-8iconv会报错并指出出错的位置。这个命令的好处是它能告诉你具体在第几行第几列出了问题比 Maven 的报错信息有用得多。在 Windows 上可以用 PowerShell 来检测Get-Content -Path src\main\resources\config.properties -Encoding UTF8 -ErrorAction Stop如果文件不是 UTF-8PowerShell 会抛出异常。3.4 第四步检查文件里的隐形杀手有些文件用file命令检测显示是 UTF-8但依然会触发Input length 1。这种情况通常是文件里混入了不可见字符。常见的隐形杀手包括BOM 头Windows 记事本保存 UTF-8 文件时会自动加上EF BB BF。虽然 BOM 本身是合法的 UTF-8 字节序列但某些工具在处理时会把 BOM 当成内容的一部分导致后续解析出错。零宽字符从网页或聊天工具复制代码时经常会带入零宽空格E2 80 8B、零宽非连接符E2 80 8C等不可见字符。控制字符比如0x00到0x1F之间的某些控制字符在 XML 和 properties 文件里是非法的。混合换行符Windows 的\r\n和 Unix 的\n混用虽然通常不会导致编码错误但在某些解析场景下会引发问题。检测这些隐形字符可以用hexdump或xxd查看文件的原始字节xxd src/main/resources/config.properties | head -20如果文件开头出现efbb bf那就是 BOM。如果中间出现e280 8b之类的序列那就是零宽字符。用grep也可以快速查找 BOMgrep -rl $\xEF\xBB\xBF src/main/resources/这个命令会列出所有带 BOM 的文件。4. 修复方案从根因出发的四种处理策略4.1 策略一统一项目编码配置推荐最根本的解决办法是在pom.xml里显式声明编码并确保所有资源文件都用同一种编码保存。标准的配置如下properties project.build.sourceEncodingUTF-8/project.build.sourceEncoding project.reporting.outputEncodingUTF-8/project.reporting.outputEncoding /properties这两个属性分别控制源码编译和报告输出的编码。maven-resources-plugin会读取project.build.sourceEncoding作为资源处理的默认编码。但光配置这个还不够。你还需要在maven-resources-plugin的配置里显式指定编码build plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-resources-plugin/artifactId version3.2.0/version configuration encodingUTF-8/encoding /configuration /plugin /plugins /build为什么要两处都配因为project.build.sourceEncoding是一个约定很多插件会读取它但不是所有插件都严格遵守。在插件自己的configuration里显式指定encoding是确保万无一失的做法。配好之后把所有资源文件统一转成 UTF-8 编码。在 Linux/macOS 上可以用iconv批量转换find src/main/resources -type f \( -name *.properties -o -name *.xml -o -name *.json \) -exec sh -c for file do iconv -f GBK -t UTF-8 $file -o $file.utf8 2/dev/null mv $file.utf8 $file done sh {} 在 Windows 上可以用 Notepad 的编码菜单批量转换或者用 PowerShell 脚本处理。4.2 策略二处理 properties 文件的特殊编码规则这里要特别说一下.properties文件。Java 的Properties类在读取文件时默认使用 ISO-8859-1 编码而不是 UTF-8。这是 Java 历史遗留问题很多人不知道。这意味着即使你把maven-resources-plugin的编码配成了 UTF-8如果.properties文件里有中文Properties.load()依然可能出问题。正确的做法有两种方案A用 Unicode 转义把.properties文件里的所有非 ASCII 字符转成\uXXXX形式。比如中文转成\u4e2d\u6587。IDEA 和 Eclipse 都有插件可以自动做这个转换。方案B用 XML 格式的 propertiesJava 支持 XML 格式的 properties 文件这种格式可以显式声明编码?xml version1.0 encodingUTF-8? !DOCTYPE properties SYSTEM http://java.sun.com/dtd/properties.dtd properties entry keyapp.name我的应用/entry /properties然后用Properties.loadFromXML()来读取。这种方式没有编码歧义但可读性稍差。方案C在代码里指定编码读取如果你能控制读取 properties 的代码可以用InputStreamReader显式指定 UTF-8Properties props new Properties(); try (InputStreamReader reader new InputStreamReader( new FileInputStream(config.properties), StandardCharsets.UTF_8)) { props.load(reader); }这种方式最灵活但需要改代码适合新项目。提示很多Input length 1的案例根源就是.properties文件里写了中文而项目编码配置和文件实际编码不一致。优先检查所有.properties文件。4.3 策略三清理 BOM 和不可见字符如果确认是 BOM 或零宽字符导致的问题需要清理这些脏字节。清理 BOM 可以用sed# 清理 UTF-8 BOM sed -i 1s/^\xEF\xBB\xBF// src/main/resources/config.properties批量清理find src/main/resources -type f -exec sed -i 1s/^\xEF\xBB\xBF// {} \;清理零宽字符可以用perlperl -i -pe s/\x{e2}\x{80}\x{8b}//g; s/\x{e2}\x{80}\x{8c}//g src/main/resources/config.properties在 Windows 上可以用 Notepad 的显示所有字符功能查看不可见字符然后手动删除。或者用 PowerShell$content [System.IO.File]::ReadAllText(config.properties) $content $content -replace [\u200B-\u200D\uFEFF], [System.IO.File]::WriteAllText(config.properties, $content, [System.Text.Encoding]::UTF8)清理完之后建议用xxd再检查一遍文件头确认 BOM 已经没了。4.4 策略四跳过不必要的资源处理有时候问题文件其实不需要被maven-resources-plugin处理。比如某些二进制文件、图片、字体文件它们本来就不是文本不应该被编码转换。这时候可以通过includes和excludes配置来跳过它们plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-resources-plugin/artifactId version3.2.0/version configuration encodingUTF-8/encoding nonFilteredFileExtensions nonFilteredFileExtensionjpg/nonFilteredFileExtension nonFilteredFileExtensionpng/nonFilteredFileExtension nonFilteredFileExtensiongif/nonFilteredFileExtension nonFilteredFileExtensionpdf/nonFilteredFileExtension nonFilteredFileExtensionzip/nonFilteredFileExtension /nonFilteredFileExtensions /configuration /pluginnonFilteredFileExtensions告诉插件这些扩展名的文件不要做 filtering直接二进制拷贝。这样就能避免插件去读这些文件的内容自然也不会触发编码错误。如果你确定某个目录下的文件完全不需要处理也可以用excludesconfiguration excludes exclude**/*.bin/exclude excludetemplates/**/exclude /excludes /configuration但要注意excludes掉的文件不会被复制到target/classes如果运行时需要这些文件就不能排除。5. 几个容易踩的坑和实测经验5.1 改了 pom.xml 但问题依旧缓存和 IDE 的干扰我遇到过好几次这样的情况明明在pom.xml里配好了 UTF-8文件也用iconv转过了但mvn clean package还是报Input length 1。后来发现是 IDE 的缓存或者 Maven 的本地仓库在捣乱。第一个坑IDE 的编码设置覆盖了 Maven 配置。IntelliJ IDEA 有自己的文件编码设置Settings → Editor → File Encodings如果这里配的是 GBK而文件实际是 UTF-8IDE 在保存文件时可能会自作主张地转换编码。解决办法是把 IDE 的编码也统一成 UTF-8并且勾选Transparent native-to-ascii conversion针对 properties 文件。第二个坑Maven 本地仓库里的旧插件缓存。有时候maven-resources-plugin的旧版本被缓存了即使你改了pom.xml里的版本号Maven 还是用旧的。解决办法是删掉本地仓库里对应的插件目录rm -rf ~/.m2/repository/org/apache/maven/plugins/maven-resources-plugin/然后重新构建让 Maven 重新下载。第三个坑target目录没清理。有时候target/classes里残留了上一次构建的旧文件导致问题看起来阴魂不散。养成习惯改完配置先mvn clean再mvn package。5.2 多模块项目里的编码传递问题在多模块 Maven 项目里父pom.xml里配的project.build.sourceEncoding不一定会自动传递给所有子模块。特别是当子模块有自己的properties覆盖时编码配置可能就丢了。我的做法是在父pom.xml里用pluginManagement统一管理maven-resources-plugin的配置build pluginManagement plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-resources-plugin/artifactId version3.2.0/version configuration encodingUTF-8/encoding /configuration /plugin /plugins /pluginManagement /build然后在每个子模块里只需要声明插件不需要重复配置build plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-resources-plugin/artifactId /plugin /plugins /build这样能保证所有子模块用同一套编码配置避免这个模块好、那个模块坏的诡异现象。5.3 从 GBK 迁移到 UTF-8 的完整操作清单如果你的项目是从 GBK 迁移到 UTF-8下面这份清单可以帮你少走弯路步骤操作验证方式1备份整个项目确认备份目录存在2修改父 pom 的 sourceEncoding 为 UTF-8mvn help:effective-pom查看3用 iconv 批量转换资源文件file -i确认编码4清理所有 BOMgrep -rl $\xEF\xBB\xBF无输出5转换 Java 源码文件IDE 批量转换或 iconv6清理 target 和本地仓库缓存mvn clean7全量构建验证mvn clean package成功8运行测试确认功能正常测试用例全部通过这份清单看起来简单但每一步都有细节。比如第3步iconv转换时如果遇到无法转换的字符会直接报错退出。这时候需要加//IGNORE参数忽略无法转换的字符但忽略之后要人工检查是否有内容丢失。第5步转换 Java 源码时要注意源码里的中文字符串常量。如果源码是 GBK直接转 UTF-8 后字符串常量会变成乱码。正确的做法是先用 IDE 的Convert encoding功能它会同时处理文件编码和字符串内容。5.4 一个真实的排查案例之前有个项目mvn clean package在本地开发机上一直报Input length 1但同事的机器上却能正常构建。我们对比了两边的环境JDK 版本、Maven 版本、pom.xml都一模一样。最后发现问题出在一个.properties文件上。这个文件里有一行注释是某位同事从网页上复制配置项时带进来的注释末尾有一个零宽空格。在同事的机器上这个零宽空格恰好被某个编辑器吃掉了所以构建正常。而在我的机器上文件是从 Git 仓库直接拉下来的零宽空格原样保留就触发了编码错误。用xxd查看那个文件在注释行末尾看到了e2 80 8b三个字节。删掉之后构建立刻恢复正常。这个案例给我的教训是不要相信在我机器上是好的这种话。编码问题往往和文件在传输、复制、保存过程中的细微差异有关。遇到这类问题第一件事是用xxd看原始字节而不是靠肉眼观察。6. 预防措施让这个报错不再出现6.1 在 CI 流程里加入编码检查与其等构建时报错不如在代码提交阶段就发现问题。可以在 CI 流程里加一个编码检查步骤#!/bin/bash # 检查所有资源文件是否为合法 UTF-8 find src/main/resources -type f \( -name *.properties -o -name *.xml -o -name *.json -o -name *.yml \) | while read file; do if ! iconv -f UTF-8 -t UTF-8 $file -o /dev/null 2/dev/null; then echo 编码错误: $file exit 1 fi done echo 所有资源文件编码检查通过这个脚本会遍历所有文本资源文件用iconv验证它们是否是合法 UTF-8。如果有问题CI 会直接失败阻止有问题的代码合并。还可以加上 BOM 检查if grep -rl $\xEF\xBB\xBF src/main/resources/; then echo 发现 BOM 文件请清理 exit 1 fi6.2 统一团队的文件保存规范编码问题的根源往往是人的问题而不是技术的问题。不同的人用不同的编辑器保存文件时用了不同的编码问题就来了。我的建议是在项目根目录放一个.editorconfig文件root true [*] charset utf-8 end_of_line lf insert_final_newline true trim_trailing_whitespace true [*.{properties,xml,json,yml,yaml}] charset utf-8.editorconfig被主流 IDE 和编辑器支持能强制统一编码、换行符、缩进等格式。虽然它不能100%杜绝问题但能大幅降低每个人保存的文件都不一样的概率。另外在项目的 README 或者开发规范文档里明确写一句所有文本文件必须使用 UTF-8 无 BOM 编码保存。这句话看起来是废话但真到了出问题的时候它能帮你省下几个小时的排查时间。6.3 升级 maven-resources-plugin 的版本maven-resources-plugin3.2.0 是2020年发布的版本虽然不算老但后续版本在错误提示方面有所改进。比如 3.3.0 之后的版本在遇到编码错误时会更明确地指出是哪个文件出了问题。如果你的项目没有特殊原因锁定在 3.2.0可以考虑升级到更新的版本plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-resources-plugin/artifactId version3.3.1/version /plugin升级前建议先在小范围测试确认没有兼容性问题。大多数情况下maven-resources-plugin的版本升级是无痛的因为它做的事情很单一。6.4 给 properties 文件加一道保险对于.properties文件我习惯在项目里加一个自定义的编码检查工具类在应用启动时扫描所有 properties 文件确认它们能被正确加载。这样即使构建阶段漏过了问题启动阶段也能快速发现。public class PropertiesEncodingChecker { public static void checkAll(Path resourceDir) throws IOException { Files.walk(resourceDir) .filter(p - p.toString().endsWith(.properties)) .forEach(p - { try (InputStreamReader reader new InputStreamReader( Files.newInputStream(p), StandardCharsets.UTF_8)) { new Properties().load(reader); } catch (IOException e) { throw new RuntimeException(Properties 文件编码错误: p, e); } }); } }这个工具类可以在单元测试里调用确保所有 properties 文件都是合法 UTF-8。虽然多写了几行代码但比起在生产环境发现配置乱码这点投入完全值得。7. 写在最后编码问题没有捷径Input length 1这个报错说到底是一个信息不足的问题。Maven 没有告诉你哪个文件出了问题你只能靠自己的排查手段去定位。我见过有人因为这个报错重装了 JDK有人换了 Maven 版本有人把整个项目重新 clone 了一遍——这些操作偶尔能碰巧解决问题但大多数时候只是浪费时间。真正有效的做法是先定位文件再分析字节最后统一编码。这三步走下来90%以上的编码问题都能解决。剩下的10%通常是文件里混入了极其隐蔽的不可见字符需要用xxd逐字节检查。我在实际项目里养成了一个习惯每次新建资源文件后先用file -i确认编码再用xxd看一眼文件头有没有 BOM。这个习惯花不了几秒钟但能避免后面几个小时的排查。编码问题就是这样预防的成本远低于修复的成本。如果你现在正被这个报错卡住不妨按这篇文章的排查链路走一遍。先从mvn process-resources缩小范围再用二分法锁定文件然后用iconv或xxd分析字节最后统一编码配置。整个过程可能需要半小时到一小时但比起盲目尝试各种偏方这条路是最稳的。