• 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());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

      我打包成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());
                    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

      
    获取完文件夹下的媒体文件路径,还要把他在项目中的相对路径转化上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():
    
    • 1

      这种方式是通过文件系统路径获取资源的,适用于本地文件系统中的资源。如果你的资源是本地文件系统中的,而不是在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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

      

    调试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());
                    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
  • 相关阅读:
    Django和jQuery,实现Ajax表格数据分页展示
    YOLOv5的Tricks | 【Trick14】YOLOv5的val.py脚本的解析
    C++ Reference: Standard C++ Library reference: C Library: cwchar: putwchar
    【数模系列】01_聚类分析
    自动化开发任务:在PHP框架中实现自定义命令
    多线程常见锁的策略
    Linux笔记之Bash脚本中的EOF
    Webfunny前端监控如何接入飞书单点登录(SSO)
    【ELFK】之消息队列kafka
    python经典百题之利用ellipse and rectangle 画图
  • 原文地址:https://blog.csdn.net/AsFarmer/article/details/134535540