JavaFx学习问题3---Jar包路径问题 (疑难杂症)

  

前置提要:

  我做了的JavaFx程序中,需要通过一个文件夹的相对路径,获取文件夹下所有音频文件的路径,把这些路径字符串放到一个List集合里,然后用Media让它播放声音。问题就出在这个文件夹路径和音频文件路径在Java程序和Jar解析有区别

解决方法:

  通过文件夹相对路径获取文件夹下媒体文件的路径
  以下代码,在idea运行是没有问题的,其中floderPath是文件夹相对路径(/xxx/xxx的形式)

try {
     // 获取资源文件夹的URL
      URL folderUrl = getClass().getResource(folderPath);

     // 使用URL创建URI,并通过URI创建Path
     Path paths= Paths.get(URI.create(folderUrl.toString()));

     // 使用Files.newDirectoryStream遍历文件夹中的文件
    try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(paths)) {
          for (Path path : directoryStream) {
              if (Files.isRegularFile(path)) {
                 filePaths.add(path.toString());
              }
                        }
                    }
                } catch (IOException e) {
                    System.out.println("在本地读取文件时发生错误:" + e.getMessage());
}

  我打包成jar包,没多想就打成exe文件了,运行exe文件的时候点击按钮发生了错误。发生错误的话,他默认会在exe同级文件夹中生成一个errorLog.txt文件(我用的是exe4j打包的,其他打包工具我不知道会怎么样)。打开文件我发现p被解析成下图这样了,多了个jar
在这里插入图片描述
  于是我用String里的substring方法,把jar:去掉或者把jar:file:/去掉,发现DirectoryStream<> directoryStream = Files.newDirectoryStream(paths)得到的directoryStream 里面的路径也是错的。

原理:

(看个大概就行)
  当Java程序在本地运行时,相对路径是相对于当前工作目录解析的。当前工作目录是指在命令行或者IDE中执行Java程序的目录。例如,如果你的Java程序文件位于/home/user/program/Main.java,而你在/home/user/目录下执行Java程序,那么相对路径resources/file.txt将被解析为/home/user/resources/file.txt。

  然而,当Java程序被打包成JAR包后运行,相对路径是相对于类路径(Classpath)。JAR包是一种压缩文件格式,它将Java程序的所有类文件和资源文件打包在一起。当Java程序在JAR包中运行时,相对路径是相对于JAR包本身解析的。

  在JAR包中,路径前面会多出来jar:的部分,这是因为Java使用jar:作为URL协议来表示JAR包中的资源。例如,如果你的JAR包文件位于/home/user/program.jar,而其中的资源文件file.txt位于JAR包的根目录下,那么相对路径file.txt将被解析为jar:/home/user/program.jar!/file.txt。这种格式的路径称为"JAR URL"。

可以改成以下这样,去判断运行环境(我不知道其他方法,这应该是比较笨的方法了)

 try {
                    // 获取资源文件夹的URL
                    URL folderUrl = getClass().getResource(folderPath);

                    if (folderUrl != null) {
                        Path p;
                        if (folderUrl.toString().startsWith("jar:")) {
                            // 如果是在JAR中运行,使用以下方式获取路径
                            FileSystem fileSystem = FileSystems.newFileSystem(URI.create(folderUrl.toString()), Collections.emptyMap());
                            p = fileSystem.getPath(folderPath);
                            System.out.println(p);
                        } else {
                            // 如果在IDE中运行,使用以下方式获取路径
                            p = Paths.get(URI.create(folderUrl.toString()));
                        }

                        // 使用Files.newDirectoryStream遍历文件夹中的文件
                        try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(p)) {
                            for (Path path : directoryStream) {
                                if (Files.isRegularFile(path)) {
                                    System.out.println("音頻----"+path);
                                    filePaths.add(path.toString());
                                }
                            }
                        }
                    } else {
                        System.out.println("资源文件夹不存在");
                    }
                } catch (IOException e) {
                    System.out.println("在读取文件时发生错误:" + e.getMessage());
                }

  
获取完文件夹下的媒体文件路径,还要把他在项目中的相对路径转化上URI让Media去播放它
有以下两种转化方式,这里同样要去区别本地Java程序和Jar包环境

  getClass().getResource("/xxx/xxx").toURI() 括号里面为相对路径
  这种方式是通过类加载器(ClassLoader)获取资源的,适用于资源在类路径下的情况。在JAR包中,资源路径以 “jar:” 开头,表示这是一个JAR URL。适用于打包成JAR的情况。
例子:getClass().getResource(“/audio/sample.wav”).toURI()

&emsp;&emsp;Paths.get(audioFilePath).toUri():

  这种方式是通过文件系统路径获取资源的,适用于本地文件系统中的资源。如果你的资源是本地文件系统中的,而不是在JAR包中,可以使用这种方式。
例子:Paths.get(“C:/path/to/audio/sample.wav”).toUri()

  我是两个都用,因为jar包解析出来的路径会有 jar: 的前缀,通过判断是否有jar:就知道运行环境是哪个,然后用if去区别执行。

/**
     * 获取音频文件相对路径
     * @param audioFilePath
     * @param uri
     * @return
     * @throws URISyntaxException
     */
    public Media mediaUri(String audioFilePath,String uri) throws URISyntaxException {
        Media media;
        URI audioUri;
        System.out.println(audioFilePath);
        if (uri.startsWith("jar:")) {
            // 获取音频文件的URI,JAR环境
            audioUri = getClass().getResource(audioFilePath).toURI();
            media = new Media(audioUri.toString());
        }else {
            // 本地Java环境
            audioUri = Paths.get(audioFilePath).toUri();
            media=new Media(audioUri.toString());
        }
        return media;
    }

  

调试JAR包

在打包成exe文件前,建议先调试jar包。网上确实有些工具是可以调试exe文件的,但不咋方便,最好还是调试Java包。在终端输入 java -jar jar路径即可启动程序,代码里面写的System.out.println(),可以在终端打印出来。
在这里插入图片描述

后续补充:

  后面在运行jar包是发现FileSystem fileSystem = FileSystems.newFileSystem(URI.create(folderUrl.toString()), Collections.emptyMap());是会去创建uri的,如果一开始就有这个uri,那么会导致重复而创建失败,修改成下面代码就可以完美解决

try {
                    // 获取资源文件夹的URL
                    URL folderUrl = getClass().getResource(folderPath);

                    if (folderUrl != null) {
                        Path p;
                        if (folderUrl.toString().startsWith("jar:")) {
                            // 如果是在JAR中运行,使用以下方式获取路径
                            try {
                                FileSystem fileSystem = FileSystems.getFileSystem(URI.create(folderUrl.toString()));
                                p = fileSystem.getPath(folderPath);
                            } catch (FileSystemNotFoundException e) {
                                // 如果FileSystem不存在,则创建新的FileSystem
                                FileSystem fileSystem = FileSystems.newFileSystem(URI.create(folderUrl.toString()), Collections.emptyMap());
                                p = fileSystem.getPath(folderPath);
                            }
                        } else {
                            // 如果在IDE中运行,使用以下方式获取路径
                            p = Paths.get(URI.create(folderUrl.toString()));
                        }

                        // 使用Files.newDirectoryStream遍历文件夹中的文件
                        try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(p)) {
                            for (Path path : directoryStream) {
                                if (Files.isRegularFile(path)) {
                                    System.out.println("音頻----"+path);
                                    filePaths.add(path.toString());
                                }
                            }
                        }
                    } else {
                        System.out.println("资源文件夹不存在");
                    }
                } catch (IOException e) {
                    System.out.println("在读取文件时发生错误:" + e.getMessage());
                }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值