Java中如何正确读取JarURLInputStream?JAR目录读取为空问题
读取JAR包内目录条目时的流为空问题
尝试读取自身JAR包内容时,使用getResourceAsStream(path)方法获取目录对应的流时,得到的sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream流为空,但读取具体文件时一切正常。
测试代码
package de.CoSoCo.testzone; import java.io.*; /** * * @author Ulf Zibis <Ulf.Zibis@CoSoCo.de> * @version */ public class ResourceAsStream { /** * @param args the command line arguments */ public static void main(String[] args) { String packagePath = ResourceAsStream.class.getPackageName().replace('.', '/'); String path; ClassLoader cl = Thread.currentThread().getContextClassLoader(); Class c = ResourceAsStream.class; InputStream in; byte [] bytes = new byte[16]; try { System.out.println("path="+( path = packagePath)); System.out.println("Class="+( in = cl.getResourceAsStream(path))); BufferedReader r = new BufferedReader(new InputStreamReader(in)); System.out.println("entries="); for (String line; (line = r.readLine()) != null; ) System.out.println(" "+line); in = cl.getResourceAsStream(path); System.out.println("read bytes="+in.read(bytes)); } catch (Exception ex) { System.out.println(ex); } try { System.out.println("path="+( path = packagePath+'/'+"A_Picture.png")); System.out.println("Class="+( in = cl.getResourceAsStream(path))); System.out.println("read bytes="+in.read(bytes)); } catch (Exception ex) { System.out.println(ex); } try { System.out.println("path="+( path = "")); System.out.println("Class="+( in = c.getResourceAsStream(path))); BufferedReader r = new BufferedReader(new InputStreamReader(in)); System.out.println("entries="); for (String line; (line = r.readLine()) != null; ) System.out.println(" "+line); in = c.getResourceAsStream(path); System.out.println("read bytes="+in.read(bytes)); } catch (Exception ex) { System.out.println(ex); } try { System.out.println("path="+( path = "A_Picture.png")); System.out.println("Class="+( in = c.getResourceAsStream(path))); System.out.println("read bytes="+in.read(bytes)); } catch (Exception ex) { System.out.println(ex); } } }
运行结果
在NetBeans IDE中以独立类文件运行时,能得到预期的目录条目:
path=de/CoSoCo/testzone Class=java.io.ByteArrayInputStream@4e50df2e entries= A_Picture.png ClassFinder.class ClassFinder$1.class FileChooserDemo.class FileTimesFromCalendar.class ResourceAsStream.class read bytes=16 path=de/CoSoCo/testzone/A_Picture.png Class=java.io.BufferedInputStream@3941a79c read bytes=16 path= Class=java.io.ByteArrayInputStream@506e1b77 entries= A_Picture.png ClassFinder.class ClassFinder$1.class FileChooserDemo.class FileTimesFromCalendar.class ResourceAsStream.class read bytes=16 path=A_Picture.png Class=java.io.BufferedInputStream@4fca772d read bytes=16
但从JAR运行时失败:
$ java -jar TestZone.jar path=de/CoSoCo/testzone Class=sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@3d4eac69 entries= read bytes=-1 path=de/CoSoCo/testzone/A_Picture.png Class=sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@135fbaa4 read bytes=16 path= Class=sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@330bedb4 entries= read bytes=-1 path=A_Picture.png Class=sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@7ea987ac read bytes=16
原因与解决方案
这不是Java的bug,而是JAR资源的设计特性。当程序在文件系统中运行时,目录是实际存在的文件对象,getResourceAsStream会返回包含目录下文件列表的流;但在JAR包中,目录并非实际的条目(除非JAR中显式包含了目录条目),getResourceAsStream针对目录路径返回的流不包含任何内容。
要读取JAR内的目录条目,正确的做法是通过getResource获取目录的URL,将其转换为JarURLConnection,然后遍历JAR中的所有条目,筛选出目标目录下的内容。示例代码如下:
import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; public class JarDirectoryReader { public static void main(String[] args) throws IOException { String packagePath = JarDirectoryReader.class.getPackageName().replace('.', '/') + "/"; URL url = JarDirectoryReader.class.getClassLoader().getResource(packagePath); if (url != null && url.getProtocol().equals("jar")) { JarURLConnection connection = (JarURLConnection) url.openConnection(); JarFile jarFile = connection.getJarFile(); Enumeration<JarEntry> entries = jarFile.entries(); System.out.println("目录下的条目:"); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String entryName = entry.getName(); // 筛选出目标目录下的条目,排除目录本身 if (entryName.startsWith(packagePath) && !entryName.equals(packagePath)) { // 去掉目录前缀,只显示文件名 String fileName = entryName.substring(packagePath.length()); // 如果是子目录,这里可以根据需求处理,比如递归遍历 if (!entry.isDirectory()) { System.out.println(" " + fileName); } } } jarFile.close(); } } }
内容的提问来源于stack exchange,提问作者CoSoCo
相关产品推荐
相关产品推荐

