【问题标题】:Reading Directory Contents From a JAR file从 JAR 文件中读取目录内容
【发布时间】:2019-12-04 06:09:54
【问题描述】:

我正在尝试在 web 应用程序中编写代码,我的类路径中有一个 JAR 文件。目的是检查目录是否存在于 JAR 中。如果是,我需要将 JAR 目录中文件的所有内容保存在 HashMap<String, String> 中。 Key 是文件名,value 是每个文件的内容。

File directory = new File(getClass().getClassLoader().getResource(directoryPath).getPath());

System.out.println("PATH IS: " + directory.getPath());

// Check if  dirPth exists and is a valid directory
if (!directory.isDirectory()) {
    throw new AccessException("Directory \"" + directoryPath + "\" not valid");
}

// Obtain a list of all files under the dirPath
File [] fileList = directory.listFiles();

for (File file : fileList) {

    if (file.isFile()) {

        // Read the file
        BufferedReader br = new BufferedReader(new FileReader(file));
        String line = null;
        StringBuilder sb = new StringBuilder();

        while ((line = br.readLine()) != null) {
            sb.append(line);
        }

        br.close();

        // Store the file data in the hash
        entry.put(file.getName(), sb.toString);
    }
}

direcotry.getPath() 的输出是:

文件:\H:\apache-tomcat-9.0.27\lib\myConfigurationFiles.jar!\META-INF\Maintenance\xmlFiles\secondary

这是我正在寻找的正确文件夹。

这里的 Map 对象是“入口”。

现在我不确定为什么 direcotry.isDirectory() 返回 false。它不应该返回 true 吗?

现在因为它没有跨越第一个异常。我不知道在那之后它会如何表现。任何帮助将不胜感激。

【问题讨论】:

  • 该 URI 实际上向您表明它是一个 inside JAR 文件,这意味着您不能以 File 的形式打开它。您不能对 JAR 中的资源使用 File 和文件操作,只能使用流操作。 jar 中没有“真正的”目录,因为它不是文件系统,它只是一个存档。
  • 这段代码有几个问题。首先,jar 文件不是目录,因此您不能像这样列出 jar 文件的内容。其次,如果在不同的上下文中运行,这段代码可能会出现问题,webapps 类加载器是复杂的野兽。
  • 我也在干预JarFile。但我所能做的就是遍历文件夹/文件名,而不是获取文件夹内的文件或读取文件的内容。

标签: java jar


【解决方案1】:
  1. getClass() 是这种工作的错误方法;如果有人子类,它会中断。正确的做法是改用MyClassName.class

  2. getClassLoader().getResource()也是错误的做法;这打破了 getClassLoader() 返回 null 的奇异但可能的情况。只需使用getResource 并稍微更改路径(添加前导斜杠,或者写入相对于您的类文件的路径)。

  3. 您将字符串file:\H:\apache-tomcat-9.0.27\lib\myConfigurationFiles.jar!\META-INF\Maintenance\xmlFiles\secondary 转换为文件名,然后询问它是否为目录。当然不是;那甚至不是一个文件。您需要进行一些字符串操作以从中提取实际的 file:您只需要 H:\apache-tomcat-9.0.27\lib\myConfigurationFiles.jar,将其提供给 java.nio.file API,然后使用它来询问它是否是文件(它永远不会是目录;jar 不是目录)。

  4. 请注意,如果您正在读取的资源不是 jar,这将不起作用。请注意,类加载 API 是抽象的:您可能会发现自己处于从头开始生成源文件或从数据库中加载源文件的场景中,getResource 方法会生成更多奇特的 URL 以进行引导。因此,这种代码根本行不通。首先确保没问题。

因此:

String urlAsString = MyClassName.class.getResource("MyClassName.class").toString(); // produces a link to yourself.

int start = urlAsString.startsWith("file:jar:") ? 8 : urlAsString.startsWith("file:") ? 4 : 0;
int end = urlAsString.lastIndexOf('!');
String jarFileLoc = urlAsString.substring(start, end);

如果您希望将其应用于实际目录(类文件等可以来自 dirs 而不是文件),您可以这样做:

var map = new HashMap<String, String>();

Path root = Paths.get(jarFileLoc);

Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
        String content = new String(Files.readAllBytes(file), StandardCharsets.UTF_8);
        map.put(root.relativize(file), content);
    }
});

对于一个实际上只是一个 zip 的罐子,它会更像:

var map = new HashMap<String, String>();
Path root = Paths.get(jarFileLoc);
try (var fileIn = Files.newInputStream(root)) {
    ZipInputStream zip = new ZipInputStream(fileIn);
    for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
        String content = new String(zip.readAllBytes(), StandardCharsets.UTF_8);
        map.put(entry.getName(), content);
    }
}

确保您知道什么是字符集并且 UTF_8 在这里是正确的。

【讨论】:

  • 好的。这是很多信息。让我过去看看!感谢您的帮助!
  • 对不起地球表面的坠落。我上周试了一下,效果很好。虽然我不得不把它调低到1.8级! :) 更不用说添加的信息真的很有帮助。谢谢!
【解决方案2】:

java.nio.file.Path 指定给您要搜索的 jar (jarPath),并将 String 指定为 jar 中的绝对目录名称 (directory),这可能对您有用:

Map<String, String> map = new HashMap<>();
try (FileSystem fs = FileSystems.newFileSystem(jarPath, null)) {
    Path dir = fs.getPath(directory);
    if (Files.exists(dir)) {
        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                throws IOException {
                map.put(file.toString(), Files.readString(file));
                return super.visitFile(file, attrs);
            }
        });
    }
}

Files.readString 适用于 Java 11+。对于早期版本,请使用:

new String(Files.readAllBytes(file), StandardCharsets.UTF_8)

【讨论】:

    猜你喜欢
    • 2012-11-22
    • 1970-01-01
    • 2013-11-27
    • 2014-10-05
    • 1970-01-01
    • 2012-04-09
    • 1970-01-01
    • 2013-06-14
    • 1970-01-01
    相关资源
    最近更新 更多