当我尝试让 InputStream 与 jar Java Sound API 中的文件连接时出现 IO 异常

Posted

技术标签:

【中文标题】当我尝试让 InputStream 与 jar Java Sound API 中的文件连接时出现 IO 异常【英文标题】:An IOexception when I try to get an InputStream connected with a file inside a jar Java Sound API 【发布时间】:2021-12-16 15:35:05 【问题描述】:

我正在编写一个使用 Java Sound API 播放声音的简单方法。我的想法是只播放 jar 中包含的 .wav 文件。有了图像,我可以使用ClassLoader.getResourceAsStream() 轻松获得InputStream 并从此流中加载图像。但是,当我对声音使用相同的方法时,我会得到一个IOException

Caused by: java.io.IOException: mark/reset not supported
    at java.base/java.util.zip.InflaterInputStream.reset(InflaterInputStream.java:290)
    at java.base/java.io.FilterInputStream.reset(FilterInputStream.java:224)
    at java.desktop/com.sun.media.sound.SunFileReader.getAudioFileFormat(SunFileReader.java:59)
    at java.desktop/com.sun.media.sound.WaveExtensibleFileReader.getAudioInputStream(WaveExtensibleFileReader.java:259)
    at java.desktop/javax.sound.sampled.Audiosystem.getAudioInputStream(AudioSystem.java:1010)
    at ru.mrkefir.stepan.engine.sound.Audio.playImpl(Audio.java:34)

原因是AudioSystem.getAudioInputStream(),我使用以下代码:

AudioInputStream stream = AudioSystem.getAudioInputStream(
     Audio.class.getClassLoader().getResourceAsStream(path)
);
Clip clip = AudioSystem.getClip();
clip.open(stream);
clip.setFramePosition(0);
clip.start();

我检查生成的 InputStream 是否可以读取,显然它不为空。正如我从异常的描述中了解到的,Java Sound API,尤其是我用来获取AudioInputStream 实例的AudioSystem 类,尝试在底层InputStream 上使用标记或重置操作。似乎这些输入流上不允许进行这些操作。

我完全糊涂了。我很奇怪javax.sound.sampled.AudioSystem 读取InputStream 的方式与javax.imageio.ImageIO 完全不同(使用ImageIO 我可以毫无问题地从罐子中读取图像)。

我该如何解决?显然,我可能会读取常规文件,而不是从 jar 中读取,但如果可能的话,我想完全从 jar 中读取声音。

我尝试使用 Java 8.0.302-open 和 Java 16.0.2-open 运行此代码。此外,如果这个问题是特定于平台的,我使用带有 Linux 内核版本 5.11.0-41-generic 的桌面 Ubuntu 20.04,不幸的是,我没有机会在其他平台上测试此代码。

已解决感谢@filpa,解决方案是将ClassLoader.getResourceAsStream() 获得的InputStream 包装到BufferedInputStream 或任何其他支持mark/reset 操作的输入流中。

【问题讨论】:

可能与this 问题及其答案有关。您是否尝试过将Audio.class.getClassLoader().getResourceAsStream(path) 包装在new BufferedInputStream() 中(或其他支持mark/resetInputStream 实现? @filpa 非常感谢!事实证明,解决方案非常简单 XD 我显然应该为自己重新发现 java.io... 【参考方案1】:

引入声音资源的更简单方法是使用getResource() 而不是getResourceAsStream()。在这种情况下无需换行。如果您向getAudioInputStream() 方法提供URL 而不是InputStream,则可以避免与标记/重置功能相关的问题/错误。

在 *** 的 info area for the javasound tag 上发布了一些示例。基本上:

URL url = YourClass.class.getResource("youraudio.wav");
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);

以上假设音频资源与 YourClass 类位于同一文件夹中。

附:如果您更喜欢答案(使用getResourceAsStream() 并换行,请实际将其写为答案并选择它。否则此问题会引诱喜欢帮助他人的人,认为此问题尚未得到回答。或者,邀请@filpa 发布他的信息作为答案并选择它。

【讨论】:

我总是推荐使用getResource(),除了JavaSound 知道如何处理(包装的)流之外,还有Clip 被缓存的可能性。如果它以 URL 开头,则不需要重新加载。 感谢@Phil Freihofner 和 AndrewThompson!我明白你想说什么。我应该选择哪种解决方案值得考虑。在我的特殊情况下,我正在编写一个非常简单的基于 Java Swing 的游戏引擎,通常用于内部教育目的。当我为一年级学生准备这些材料时,我想表现并不重要。不过,再次感谢您的帮助,我会为自己的发展考虑选择。我会将这个答案标记为不打扰 filpa 的解决方案。 游戏循环是学生项目的好主意! FWIW,在使用 JavaFX 和 LibGDX/LWJGL 之前的几年中,jvm-gaming.org 上可能有一些成员编写的文章和代码,可以参考他们的想法(在他们的“文章和教程”部分)。祝您教学顺利!如果@filpa 主动提供他的答案作为答案,请随时切换您的答案名称。要点:感谢您更新此问题的状态。 FWIW 我完全同意这一点,再加上@AndrewThompson 的评论,这是公认的答案,因为它比我的评论(在技术上不适合代替重复投票)提供了更多的洞察力。 “否则这个问题会吸引那些喜欢帮助别人的人,认为这个问题还没有得到回答。” 呵呵..我建议将答案标记为已接受以后寻找答案的人受益。就我而言,我可能会得到一个包含 20 个问题的列表 - 但只会查看带有绿色勾号的列表!

以上是关于当我尝试让 InputStream 与 jar Java Sound API 中的文件连接时出现 IO 异常的主要内容,如果未能解决你的问题,请参考以下文章

当 jar 文件从 URL 作为 InputStream 打开时,JarEntry.getSize() 返回 -1

来自 jar-File 的 InputStream 始终返回 null

Eclipse 需要哪些 JAR 文件才能使用 JSTL 才能最终在 GAE/J 上运行?

尝试运行 jar 文件时没有主要清单属性

Springboot通过Jar与War的打包与部署

Springboot通过Jar与War的打包与部署