Maven打包导致resources二进制文件损坏

本帖参考: https://blog.csdn.net/skyupward/article/details/58693086

问题描述

今天接了老板的一个单子,要求我使用NoteBlockAPI作为前置,给玩家放置在地上的方块添加一段音乐。让玩家靠近这个方块时播放那段音乐。
你可以使用这个工具将一段mid音频文件转化为nbs文件。之后就可以用NoteBlockAPI来读取这个nbs文件,并转化为Java对象,调用play方法即可为玩家播放音符盒制成的音乐。
通过前面那一小段介绍,相信你已经了解到了,NoteBlockAPI非常方便易用,如果对这个API感兴趣的话可以点进它的github页面查看使用方法,但我们这里要讨论的并不是这个问题。

我很快得将插件主要代码编写完毕,并对它进行测试。当我把方块放置在地上,我发现并没有如我预期中的那样播放了音乐。于是我开始检查控制台输出,想要找到出错的原因。
果然,在我的插件启动过程中,有这样一行输出:

1
2
3
[00:30:43] [Server thread/INFO]: [MusicBlock] 创建歌曲文件夹...
[00:30:43] [Server thread/INFO]: Song is corrupted: Undertale - Megalovania.nbs
[00:30:43] [Server thread/INFO]: [MusicBlock] 创建存档文件夹...

它告诉我,歌曲文件已损坏。

为了让老板把插件放进服务端时能够即时测试,我在插件内部放置了一个.nbs文件,当插件第一次启动时便会将这个.nbs文件放置到/musics目录下,这样我的老板就会知道这个目录是用于放置nbs文件的。
但是现在它告诉我,我插件内置的这个nbs文件已损坏。于是我开始检查,最后发现/musics的那个nbs文件,和我源码中的nbs文件大小有些许差异。
于是我直接把源码工程文件夹中的那个nbs文件复制到plugins/MusicBlock/musics中,重启插件,一切顺利。
再等我写完这个插件的其他功能之后,我又转过头来研究这个问题。

试想一下,如果这个插件中的一个测试资源都无法使用的话,老板会怎么想。
我当然也可以直接给老板解释,这个插件中的测试nbs文件是坏的,你直接把自己的音乐文件放置在musics目录下即可。
但……这样也未免显得我太不专业了。

问题排查

于是我开始检查我的代码,我觉得可能是文件复制时出现了一些错误。
一开始我是这样子写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
File musicFolder = new File(plugin.getDataFolder(), "musics");
if (musicFolder.mkdirs()) {
Bukkit.getConsoleSender().sendMessage("§e[MusicBlock] 创建歌曲文件夹...");
try {
Files.copy(
plugin.getResource("Undertale - Megalovania.nbs"),
new File(musicFolder, "Undertale - Megalovania.nbs").toPath(),
StandardCopyOption.REPLACE_EXISTING
);
} catch (Exception e) {
e.printStackTrace();
}
}

这个恼人的bug让我一度以为是JDK自己出现了bug —— 我怀疑可能是 Files.copy()这个方法里面有些bug。
因此我尝试自己手动去写这一个文件复制的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
File musicFolder = new File(plugin.getDataFolder(), "musics");
if (musicFolder.mkdirs()) {
Bukkit.getConsoleSender().sendMessage("§e[MusicBlock] 创建歌曲文件夹...");
File file = new File(musicFolder, "Undertale - Megalovania.nbs");
try {
int size;
byte[] bytes = new byte[1024];
InputStream inputStream = plugin.getResource("Undertale - Megalovania.nbs");
FileOutputStream fileOutputStream = new FileOutputStream(file);
while (true) {
size = inputStream.read(bytes);
if (size <= 0) {
break;
}
fileOutputStream.write(bytes, 0, size);
}
fileOutputStream.flush();
fileOutputStream.close();
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}

修改完这段复制的代码之后,我发现那里仍然会提示我文件损坏。
然后我又转念一想,也许代码本身没有问题,源码文件夹中的那个nbs也没有问题。
那么,问题只可能出现在编译完成后的那个.jar文件中了,也许是我用maven编译时,maven对这个文件动了手脚。
于是我飞快地打开了百度,输入了三个关键字: Maven resources 损坏
按下回车搜索
第一篇博客就让我看到了答案。

原因

好吧,说白了就是maven在打包我的插件时,会自动替换resources文件下的一些东西。
比如说我的plugin.yml:

1
2
3
4
name: MusicBlock
version: ${project.version}
main: cn.hamster3.musicblock.MusicBlock
depend: [NoteBlockAPI]

maven在打包编译我的插件时,会自动将那个${project.version}替换成pom.xml中设定的项目版本。
当然,maven并不总是知道它在做什么,有时候他会把你的一些二进制文件也修改了,最后导致你的文件(图像、dll和这里的nbs)损坏。
当然,这个替换是可以手动关闭的:

1
2
3
4
5
6
7
8
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>

只要把那个filtering由true改为false即可,这样maven就不会碰你的任何resources文件了。
但是……这样又显得不太优雅,以前每一次版本更新我都只需要修改一下pom.xml中的version标签,现在如果把filtering关了的话,这一个项目打包时,我又得额外再手动更改plugin.ymlversion了。

解决办法

所幸那篇博客告诉了我第二种解决方法,我们可以安装一个maven插件,这一点也不复杂,只需要修改一下pom.xml即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>nbs</nonFilteredFileExtension>
<nonFilteredFileExtension>pdf</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
</plugins>
</build>

project中的build标签里,有一个plugins标签,这一个标签中可以自定义maven插件。
我们这里新增一个maven-resources-plugin插件,并在该插件的configuration标签中设置filter不影响哪些文件。
上面的展示代码中,我设置了filter不影响.nbs和.pdf后缀的文件。
现在,我们既可以享受maven打包时filter带来的便利,也不用再担心filter会去修改不该动的文件了。

人生不易,仓鼠断气