前置提要:
我做了的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()
  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());
}