将大型资源文件夹包含到 java 项目中的最佳做法

Best practices for including large resource folder into java project

我正在做一个需要访问大型资源文件夹(包含数千张小图像的结构)的项目。客户希望通过本机安装(包括应用程序需要 运行 的 JVM)提供应用程序。他不想将该资源打包为应用程序中的文件夹,因为它会在最终用户的硬盘驱动器中创建一个与原始文件夹一样大的文件夹结构(该文件夹占用的空间不多 space 但它有很多小文件),再加上文件夹很容易通过简单的复制被窃取。鉴于此,据我所知,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" <--- URL return null 但至少不会引发 NullPointerException。

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:使用 class JarFile 的一些复杂代码对我不起作用,旨在获取一个简单的文件,而不是文件夹。获得here

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

然后通常会将图像作为只读资源打包在它自己的 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 必须在 class 路径上。

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);
        }
    }

我的错误:

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

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

  • 我使用 java JarFile class 来管理内容,但是这个 class 不提供管理文件的方法,例如 java 文件class 确实如此。所以它不像处理另一种文件那样容易。

我做了什么:
我开发了一个代码来读取 .jar 中的所有文件条目,区分我感兴趣的那些。然后将它们解压缩到应用程序内的目录中。通过这样做,我可以对它们进行标准访问,如果我愿意,我可以在应用程序关闭时简单地删除目录。我试图直接从 jar 中使用它们,但 jar 文件是 zip 文件,所以在某些时候需要将内部文件从 jar 中提取到某个地方,就像 OS 处理 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());
    }
  }
}