【问题标题】:Best practices for including large resource folder into java project将大型资源文件夹包含到 java 项目中的最佳实践
【发布时间】:2019-01-03 06:14:20
【问题描述】:

我正在开展一个需要访问大型资源文件夹(包含数千个小图像的结构)的项目。客户希望通过本机安装(包括应用程序运行所需的 JVM)来提供应用程序。他不想将这些资源打包为应用程序中的文件夹,因为它会在最终用户的硬盘驱动器中创建一个与原始文件夹一样大的文件夹结构(该文件夹不占用太多空间,但它有很多小文件),另外,只需复制该文件夹即可轻松窃取该文件夹。鉴于此,我无法将所有应用程序与资源文件夹打包在 jar 文件中,据我所知 jar 文件不可安装。另一个要求是客户端需要一定的灵活性来在已安装的应用程序文件夹结构中添加一些文件以向程序添加新功能。所以安装是获得这个的唯一方法(我认为)。

我尝试将它们打包到一个 jar 文件中,将其包含在构建路径中并尝试访问它,但即使我通过各种站点进行了所有研究,我也失败了。以一百万种方式尝试了 getResources() ,但是不可能在 jar 文件中获取一个简单的目录,同时从 jar 外部的文件夹中获取它真的很容易。我需要访问一个目录才能获得它包含的文件列表。

到此为止。我已经开始问自己,我是否以最好的方式面对这个问题,所以我想问大家:你将如何将你需要的资源打包到具有这种要求的原生 java 应用程序中?

我什至在考虑创建某种加密过程来创建一个包含所有信息的单个文件,并在运行时需要时简单地对其进行临时解密,但我认为会有一种更简单、更简洁的方法来解决这个问题。

提前谢谢你

编辑:正如你所要求的,我正在添加我尝试过的代码:

这是项目结构

project
├───src
│   ├───main
│   │   └───java
│   │       ├───model <--- where my class is
│   │       ├───controllers
│   │       ├───utilities
│   │       └───views
│   ├───resources <--- this is where is (formerly) accessed the content i need
|   |   ├─── jarfile.jar <--- i've placed file here and included to build path
│   │   └───-various folders and files -
│   └───test
└───target

jar 文件里面有包 src.resources.blalblaba,里面是我需要的文件夹

方式一:

getResources 替换 jar 文件 "."使用“/”尝试使用路径:“src/resources/blablabla/folderINeed”、“src/resources/src/resources/blablabla”(由于可能的重复性)、“folderINeed”、“blablabla/folderINeed”-> URI 总是得到带有消息“null”的 NullPointerException

public void loadContent(String contentPath) throws Exception
{ 
    File resources= null;
    File[] listFiles = null;

    URI uri = getClass().getClassLoader().getResource(contentPath).toURI();
    resources= new File(uri);
    listFiles = resources.listFiles();

    //do some file proccessing and load them
}

方式 2:使用的路径 "folderINeed","src/resources/blablabla/folderINeed","blablabla/folderINeed","../../../resources/blablabla/folderINeed"

public void loadContent(String contentPath) throws Exception
{ 
    // conseguimos todas las carpetas de animaciones
    File resources;
    File[] listFiles = null;

    URL url = MyClass.class.getResource(contentPath);
    if (url == null) {
         // error - missing folder
    } else {
        resources = new File(url.toURI());
        listFiles = resources.listFiles();
    }
}

方式 3:使用类 JarFile 的一些复杂代码对我不起作用,并且旨在获取简单文件,而不是文件夹。获得here

【问题讨论】:

  • 将图像打包到 .jar 文件中是处理此问题的正确方法。请更新一个问题,并至少包含您尝试使用 getResources() 的一百万种方法中的一种,以及对您不起作用的 jar 文件的结构。
  • 好的,我会这样做@yole。
  • 不要将 .jar 文件放在资源文件夹中。相反,将整个应用程序连同 .class 文件和图像一起打包成一个 .jar 文件。
  • @Yole 我已经考虑过了,但是客户希望通过安装程序来展示应用程序,以便可以沿着运行它所需的 JVM 以本机方式安装它。给出这个我认为这是不可能的。我将编辑添加此详细信息的问题
  • 这怎么可能? Windows 和 Mac 上的 IntelliJ IDEA 就是这样分发的。您有一个安装 JRE 和一堆 .jar 文件的本机安装程序,其中一些包含 .class 文件,而另一些包含图像。 (也可以是单个 .jar 文件 - 没关系。)

标签: java jar resources packaging requirements


【解决方案1】:

src/main/java/ma​​ven 的构建约定,所以我假设是这样。

然后通常会将图像作为只读资源打包在自己的 jar 中,这是一个单独的 maven 项目jarfile

jarfile/src/main/resources/ 将是图像的根目录。例如: jarfile/src/main/resources/icons16/new.png 然后会被类似的东西访问:

getClass().getResource("/icons16/new.png"); // Class' package relative path
getClass().getResourceAsStream("/icons16/new.png");
getClassLoader().getResource("icons16/new.png"); // Absolute class path path

在原始项目中,可以在 pom.xml 中将 dependency 添加到 jarfile。

如果不使用maven,jarfile.jar 必须在类路径中。

File 不能与资源一起使用,但 getResourceAsStream 通常是可行的。


要读取资源目录,目录路径应该是唯一的, 它只是目录资源中的文件名列表。

    InputStream in = App.class.getResourceAsStream("/icons16");
    try (BufferedReader rd = new BufferedReader(new InputStreamReader(in, "UTF-8"))) {
        String line;
        while ((line = rd.readLine()) != null) {
            System.out.println("line: " + line);
        }
    }

【讨论】:

  • 是的,你是对的,这是一个 Maven 项目。我非常感谢您的努力,但是正如我在问题中所说,我需要访问一个文件夹才能列出它的所有内容而不是逐个文件,因为 1)我不知道将来会包含什么资源文件夹。这样我们可以在其他版本的资源文件夹中添加更多材料,并且 2)有很多文件需要硬编码。鉴于此,我没有成功使用 getResource 或 getResourceAsStream 访问文件夹。
  • 我已经通过阅读资源目录添加了答案。我一般不做这样的事情,不知道这是否是官方的解决方案。需要查找 java 参考。
  • getResource(...) 提供了一个类似jar:file://.... jarfile.jar!icons16/new.png 的 URL,可以通过它创建文件系统视图。这样既能更独特地识别罐子,也更正常。不过我今天没时间了。
  • 我已经尝试过,没有添加 maven 依赖项并将“/icons16”替换为“folderINeed”,但在输入流中仍然为空。我正在研究如何向 Maven 添加本地依赖,然后重试。
  • 好的,用一些zip工具打开jar(zip格式)并检查路径;区分大小写等:斜杠 /、带前导 / 的类、不带前导 / 的 ClassLoader。
【解决方案2】:

我的错误:

  • 正如@Joop Egen 在他的回答中所说,我的问题之一是我在项目中的文件夹结构。我没有遵循将资源文件夹放在 src/main/ 文件夹中的 maven 约定,这是因为我尝试的所有解决方案都没有获取资源文件夹的必要范围。

  • 我不知道 jar 文件如何与 java 一起工作。对于 java .jar 文件是其中每个文件的 jar 条目的集合(不是 java 的集合)。在我的单个案例中,.jar 文件是使用 Eclipse 导出向导创建的,并且没有对文件夹的任何引用,它只是对文件的引用。因此,要获得一个包含所有内容的文件夹是不可能的。

  • 我使用了 java JarFile 类来管理内容,但是这个类没有像 java File 类那样提供管理文件的方法。所以它不像其他类型的文件那样容易。

我做了什么:
我开发了一个代码来读取 .jar 中的所有文件条目,区分我感兴趣的那些。然后将它们提取到应用程序内的目录中。通过这样做,我可以对它们进行标准访问,如果我愿意,我可以在应用程序关闭时简单地删除目录。我试图直接从 jar 中使用它们,但 jar 文件是 zip 文件,因此在某些时候需要将内部文件从 jar 提取到某个位置,就像操作系统对 zip 文件所做的那样。它可以是临时目录,也可以不是。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.commons.io.FileUtils;

public class App
{
  public static void main(String[] args)
  {
    try
    {

      //needed attributes
      String pathToJar = "./src/main/blablalba/fileName.jar";
      String folderYouWantToRetrieveFromTheJar = "/folderIWant";
      String pathOfFilesWithinTheJar="src/resources/blablabla/"+folderYouWantToRetrieveFromTheJar+"/";
      String tempDirectoryWhereExtract="./src/main/resources/temp";

      //creating the temp directory
      File tempDirectoryReference = new File(tempDirectoryWhereExtract);
      if (!tempDirectoryReference.exists())
      {
        Files.createDirectory(tempDirectoryReference.toPath());
      }

      //searching what entries i need
      JarFile jar = new JarFile(pathToJar);
      final Enumeration<JarEntry> entries = jar.entries(); 
      List<JarEntry> targetEntries = new ArrayList<>();
      while (entries.hasMoreElements())
      {
        JarEntry entry = entries.nextElement();
        //if the entry is what i need 
        if (entry.getName().startsWith(pathOfFilesWithinTheJar))
        { 
          targetEntries.add(entry);
        }
      }
      //extract every target entry from the .jar
      for (JarEntry entry : targetEntries)
      {
        //in order to copy the structure i will get only the point where folderIWant is present
        int index = entry.getName().indexOf(folderYouWantToRetrieveFromTheJar);
        String newTemporaryPath = tempDirectoryReference.getPath().toString()+"/"+entry.getName().substring(index);
        extractFileFromJar(jar, entry, new File(newTemporaryPath));

      }

      jar.close();
      //(optional) clean after use
      FileUtils.deleteDirectory(tempDirectoryReference);


    }
    catch (IOException e)
    {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  public static void extractFileFromJar (JarFile jarFile, JarEntry targetEntry, File destinyPath)
  {
    try
    {
      if (!destinyPath.getParentFile().exists())
      {
        createFolderStructure(destinyPath);
      }
      else
      {
        Files.createFile(destinyPath.toPath());
      }

      InputStream inputStream = jarFile.getInputStream(targetEntry); 
      FileOutputStream outputStream = new java.io.FileOutputStream(destinyPath);
      while (inputStream.available() > 0) {  
          outputStream.write(inputStream.read());
      }
      outputStream.flush();
      outputStream.close();
      inputStream.close();
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }
  }


  private static void createFolderStructure(File destinyPath)
  {
    File parentPath = destinyPath.getParentFile();
    try
    {
      if (parentPath.exists())
      {
          Files.createFile(destinyPath.toPath());
      }
      else
      {
        Files.createDirectories(destinyPath.getParentFile().toPath());
        Files.createFile(destinyPath.toPath());
      }
    }
    catch(IOException e)
    {
      System.err.println(e.getMessage());
    }
  }
}

【讨论】:

    猜你喜欢
    • 2021-12-28
    • 2015-12-11
    • 1970-01-01
    • 1970-01-01
    • 2013-11-18
    • 2022-01-04
    • 2018-03-03
    • 2010-12-24
    • 1970-01-01
    相关资源
    最近更新 更多