JavaFX打包后读取jar中的resources文件 如:music

2,776 阅读2分钟

一切都源自在我的组长叫我写个桌面应用,由于我只会Java,所以他叫我用Java写,于是在一顿搜索中找到了JavaFX这个java最后的倔强
在写的一个音乐播放器的小 demo 中,一开始开发的时候,什么问题都没有,在后面打包成 .exe 文件后却运行不了,于是在一顿百度下通过在 idea debug打包的 jar 包,发现问题出在 加载 resources/music 下的音乐文件的时候出错

GITHUB 项目地址](github.com/Jratil/Java…)
仿照spring 使用最简单的自动注入的方式
在每个用到其他 controllercontroller 类中 @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());
    }
}