有没有办法判断类路径资源是文件还是目录?
例如,假设com.google包存在于某个JAR(例如,Guava)中,此片段会在stream.read()
行上抛出NullPointerException(!)。
ClassLoader classLoader = getClass().getClassLoader(); URL resource = classLoader.getResource("com/google"); InputStream stream = resource.openStream(); System.out.println(stream.toString()); // Fine -- stream is not null stream.read(); // NPE inside FilterInputStream.read()!
如果com/google
与文件系统中的软件包而不是JAR交换,那么该代码段根本不会崩溃。 事实上,它似乎读取了该目录中的文件,由换行符分隔,但我无法想象在任何地方都指定了行为。
如果资源路径“com / google”指向“普通”资源文件或目录,是否有方法测试?
由于加载这些资源所涉及的协议处理程序的一些未指定的行为,这有点乱。 在这种特殊情况下,有两个: sun.net.www.protocol.file.Handler
和sun.net.www.protocol.jar.Handler
,它们每个处理目录的情况略有不同。 基于一些实验,这是他们每个人做的事情:
sun.net.www.protocol.file.Handler :
-
这个
Handler
做的是打开一个FileURLConnection
,它完全按照你在面对目录时发现的那样做 。 您可以检查它是否只是一个目录:if (resource.getProtocol().equals("file")) { return new File(resource.getPath()).isDirectory(); }
sun.net.www.protocol.jar.Handler :
- 另一方面,这个
Handler
打开一个JarURLConnection
,最终进入ZipCoder
。 如果你看看那段代码,你会发现一些有趣的东西:jzentry
将从原生JNI调用返回null
,因为JAR zip文件实际上并没有包含一个名为com/google
的文件,所以它返回null到包裹它的流。
但是,有一个解决方案。 虽然ZipCoder
找不到com/google
,但它会找到com/google/
(这是大多数ZIP接口工作的原因,出于某种原因)。 在这种情况下,将找到jzentry
,它只返回一个空字节。
因此,通过所有这些随机特定于实现的行为,您可以通过首先尝试使用尾随/
( 这是URLClassLoader
对目录所期望的 )访问资源来弄清楚它是否是目录 。 如果ClassLoader.getResource()
返回非null,那么它是一个目录。 如果没有,请尝试不使用尾部斜杠。 如果它返回非null,则它是一个文件。 如果它仍然返回null,那么它甚至不是现有资源。
有点hacky,但我认为没有更好的。 我希望这有帮助!
没有安全和通用的方法来检测这一点。 当您使用ClassLoader.getResource()时,ClassLoader几乎可以返回URL中的任何内容,原则上甚至是ClassLoader实现其自己的URL方案(和协议)时您从未见过的内容。
你唯一的选择是分析getResource()返回的URL,协议应该暗示它是什么(例如“file://”)。 但要注意,根据环境,它可能会返回您没有计划的东西。
但是只是访问资源,你不关心它来自哪里(如果你正在调试配置问题,你可能会在乎,但你的代码应该不在乎)。
一般情况下,您不应该对返回的InputStream的function做出假设,即不要依赖它支持标记/重置等。唯一安全的操作就是简单地读取Stream。 如果在读取期间发生IOException,则表示访问资源存在问题(网络连接丢失等)。
编辑:getResource()应该IMO只返回资源(例如文件或zip文件条目),但永远不会返回目录 (因为它们不是资源)。 但是我不会指望每个可能的ClassLoader这样做,我不确定正确的行为是什么(如果它甚至指定在某处)。
我认为有两种解决方案。
- 朴素的解决方案基于路径本身的分析。 如果它以
.jar
或.zip
或.war
或.ear
结尾,则它是一个文件。 否则它是一个目录。 我认为这种方法在99.99%的情况下都有效,除非有人试图让你故意失败。 例如,通过定义看起来像目录但是文件的软链接,反之亦然。 - 尝试模拟相对于当前工作目录解释类路径路径的JVM逻辑。 因此,使用
new File(".")
检索当前工作目录,然后使用类路径,拆分它,并为每个元素使用new File(".", classPathElement)
除非使用绝对路径定义。
祝你好运。