一切都源自在我的组长叫我写个桌面应用,由于我只会Java,所以他叫我用Java写,于是在一顿搜索中找到了JavaFX这个java最后的倔强
在写的一个音乐播放器的小 demo 中,一开始开发的时候,什么问题都没有,在后面打包成 .exe 文件后却运行不了,于是在一顿百度下通过在 idea debug打包的 jar 包,发现问题出在 加载 resources/music 下的音乐文件的时候出错
GITHUB 项目地址](github.com/Jratil/Java…)
仿照spring 使用最简单的自动注入的方式
在每个用到其他controller的controller类中@Autowired Controller controller注入
一开始我是通过
new File(getClass().getResources("/music"))
加载的文件,打包后发现加载不出来,于是又是一顿百度,都是说使用
new File(getClass().getResourcesAsstream("..."))
来获取文件,但是 JavaFX 的 Media 只有一个构造参数
public Media(@NamedArg("source") String source);
其内部是通过
URI uri = new URI(source);
来获取文件的
所以上面这个 getResourcesAsstream() 行不通
于是又是一顿百度,终于找到了方法,主要就是下面的getMusicInfoFromJar() (写了一个工具栏,其中一些无关方法被删了)
public class PathUtils {
// 其中 MusicInfo 是我简单的封装的音乐信息的实体类
public static List<MusicInfo> getMusicInfoList(String relativePath) throws IOException {
if (isJar()) {
return getMusicInfoFromJar(relativePath);
}else {
return getMusicInfoFromLocal(relativePath);
}
}
/**
* 获取开发时存放的音乐文件
*
* @param relativePath 开发时音乐文件的目录
* @return 获取到的音乐文件包装后的 {@link MusicInfo}
*/
private static List<MusicInfo> getMusicInfoFromLocal(String relativePath) {
List<MusicInfo> musicList = new ArrayList<>();
File files = new File(PathUtils.class.getResource(relativePath).getPath());
for (File file : files.listFiles()) {
String musicName = file.getName();
String musicPath = file.getAbsolutePath();
musicPath = musicPath.replaceAll("\\\\", "/");
musicPath = musicPath.substring(musicPath.indexOf("/music"));
MusicInfo musicInfo = new MusicInfo(musicName, musicPath);
musicList.add(musicInfo);
}
return musicList;
}
// 主要说这个方法
// Media 是可以支持 jar文件路径的,这个下面再说,先说代码
/**
* 从 Jar 包获取文件的方法
* @param relativePath 音乐文件的目录
* @return
* @throws IOException
*/
private static List<MusicInfo> getMusicInfoFromJar(String relativePath) throws IOException {
List<MusicInfo> musicList = new ArrayList<>();
// 为了方便看,我把下面三个方法都放上来了
// 这是拼接的 jar文件路径
// 拼接后大概是 jar:file:D:/code/demo.jar!/music
String jarPath = PathUtils.class.getProtectionDomain().getCodeSource().getLocation().getPath();
File file = new File(jarPath);
// 得到一个 JarFile,然后根据此可以获取到其下面的所有目录文件
// 再通过遍历和过滤得到 /music 目录以及下面的文件
JarFile jarFile = new JarFile(file);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String innerPath = entry.getName();
// 过滤只要 'music/' 下的文件,并且过滤掉 'music/' 这个空目录
if (innerPath.startsWith("music/") && !innerPath.endsWith("music/")) {
String musicName = innerPath.substring(innerPath.indexOf("/") + 1);
String musicPath = "/music/" + musicName;
// 其中歌曲名字只储存文件名
// 歌曲路径为 /music/name.mp3 这种
MusicInfo musicInfo = new MusicInfo(musicName, musicPath);
musicList.add(musicInfo);
}
}
return musicList;
}
private static Enumeration<JarEntry> getJarEntries() throws IOException {
String jarPath = getJarPath();
File file = new File(jarPath);
JarFile jarFile = new JarFile(file);
return jarFile.entries();
}
/**
* 如果不是 jar包,会获取到 /target/classes 目录
* 因此可以通过此判断是否是 jar 还是开发
* @return
*/
public static String getJarPath() {
return PathUtils.class.getProtectionDomain().getCodeSource().getLocation().getPath();
}
/**
* 判断是不是从 Jar 包读取的情况
* 如果是,则该路径下的不是文件夹
* @return
*/
public static boolean isJar() {
return !(new File(getJarPath()).isDirectory());
}
}
接下来是播放的 controller 中加载 media 方法
因为 Media 可以加载 Jar 路径的文件,具体可以去官方文档看
所以我们只要得到路径就好
因为我 MusicInfo 的 filePath只存了/music/**.mp3
所以这边拼接成jar:file:D:/code/demo.jar!/music/**.mp3的就可以加载到 media 中了
private Media getMedia(String path) {
if (PathUtils.isJar()) {
path = String.format("jar:file:%s!%s", PathUtils.getJarPath(), path);
return new Media(path);
} else {
return new Media(PathUtils.class.getResource(path).toExternalForm());
}
}