可执行Java Jar无法读取资源路径,加载MP3文件失败求助
问题描述
在VS Code中运行Java应用时,读取/sounds/music/MainThemes目录下的MP3文件一切正常,但生成Jar包执行后,无法正常加载文件。通过日志打印发现,代码执行到--- 2 ---后无法进入while循环,BufferedReader读取流直接返回null,导致mainThemeMediaList列表未被填充。
需求是读取/sounds/music/MainThemes下所有.mp3文件并添加到列表中,相关代码如下:
private void loadMainThemes() throws AudioLoadingException { try { System.out.println("--- 1 ---"); InputStream inputStream = getClass().getResourceAsStream("/sounds/music/MainThemes"); System.out.println(inputStream); if (inputStream != null) { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); System.out.println(reader); String line; System.out.println("--- 2 ---"); while ((line = reader.readLine()) != null) { System.out.println("--- 3 ---"); if (line.endsWith(".mp3")) { System.out.println("--- 4 ---"); Media media = new Media(getClass().getResource("/sounds/music/MainThemes/" + line).toString()); System.out.println("--- 5 ---"); mainThemeMediaList.add(media); } } } else { throw new AudioLoadingException("MainThemes resource not found."); } } catch (IOException e) { throw new AudioLoadingException("Failed to load MainThemes. " + e.getMessage()); } }
问题原因
IDE环境中,/sounds/music/MainThemes是文件系统中的真实目录,getResourceAsStream会返回该目录的列表流,BufferedReader可以读取到目录下的文件名。但打包成Jar后,资源是压缩包内的条目,无法通过读取目录流的方式获取内部文件列表——此时inputStream虽不为null,但读取到的内容并非文件名列表,直接返回null,导致循环无法执行。
解决方案
方法1:纯Java通用方案(兼容IDE与Jar环境)
该方法自动识别运行环境,分别处理文件系统(IDE)和Jar包两种情况:
private void loadMainThemes() throws AudioLoadingException { try { String basePath = "/sounds/music/MainThemes"; URL resourceUrl = getClass().getResource(basePath); if (resourceUrl == null) { throw new AudioLoadingException("MainThemes resource not found."); } if ("file".equals(resourceUrl.getProtocol())) { // IDE环境:直接遍历文件系统目录 File dir = new File(resourceUrl.toURI()); File[] mp3Files = dir.listFiles((file) -> file.getName().endsWith(".mp3")); if (mp3Files != null) { for (File file : mp3Files) { Media media = new Media(getClass().getResource(basePath + "/" + file.getName()).toString()); mainThemeMediaList.add(media); } } } else if ("jar".equals(resourceUrl.getProtocol())) { // Jar环境:解析Jar包内的资源条目 String jarPath = resourceUrl.getPath().substring(5, resourceUrl.getPath().indexOf("!")); JarFile jarFile = new JarFile(jarPath); Enumeration<JarEntry> entries = jarFile.entries(); String targetDir = basePath.substring(1) + "/"; // 去掉开头的/,拼接目录后缀 while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String entryName = entry.getName(); // 筛选目标目录下的MP3文件,排除目录本身 if (entryName.startsWith(targetDir) && entryName.endsWith(".mp3") && !entry.isDirectory()) { String fileName = entryName.substring(targetDir.length()); Media media = new Media(getClass().getResource(basePath + "/" + fileName).toString()); mainThemeMediaList.add(media); } } jarFile.close(); } } catch (URISyntaxException | IOException e) { throw new AudioLoadingException("Failed to load MainThemes. " + e.getMessage()); } }
方法2:Jar环境专属方案
如果确定应用仅以Jar包形式运行,可以直接枚举Jar内的资源条目:
private void loadMainThemes() throws AudioLoadingException { try { // 获取当前Jar文件路径 String jarPath = getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath(); JarFile jarFile = new JarFile(jarPath); Enumeration<JarEntry> entries = jarFile.entries(); String targetDir = "sounds/music/MainThemes/"; while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String entryName = entry.getName(); if (entryName.startsWith(targetDir) && entryName.endsWith(".mp3") && !entry.isDirectory()) { Media media = new Media(getClass().getResource("/" + entryName).toString()); mainThemeMediaList.add(media); } } jarFile.close(); } catch (URISyntaxException | IOException e) { throw new AudioLoadingException("Failed to load MainThemes. " + e.getMessage()); } }
方法3:Spring环境下的简化方案
如果项目依赖Spring框架,可以用PathMatchingResourcePatternResolver快速扫描资源,自动兼容两种环境:
private void loadMainThemes() throws AudioLoadingException { try { PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); // 匹配目标目录下所有MP3文件 Resource[] resources = resolver.getResources("classpath:/sounds/music/MainThemes/*.mp3"); for (Resource resource : resources) { Media media = new Media(resource.getURL().toString()); mainThemeMediaList.add(media); } } catch (IOException e) { throw new AudioLoadingException("Failed to load MainThemes. " + e.getMessage()); } }
内容的提问来源于stack exchange,提问作者Shodan4004
相关产品推荐
相关产品推荐

