简体   繁体   English

Java 读写资源文件夹

[英]Java read and write to the resource folder

When someone opens my jar up I am opening a file selector gui so they can choose where they want to store their jar's files like config files and such.当有人打开我的 jar 时,我正在打开一个文件选择器 gui,这样他们就可以选择他们想要存储 jar 文件(如配置文件等)的位置。 This should only take place the first time they open the jar.这应该只在他们第一次打开 jar 时发生。 However, one issue with this approach is that I would have no way to know if it's their first time opening the jar since I will need to save the selected path somewhere.但是,这种方法的一个问题是我无法知道这是否是他们第一次打开 jar,因为我需要将选定的路径保存在某处。 The best solution to this sounds like saving the selected path inside a file in the resource folder which is what I am having issues with.对此的最佳解决方案听起来像是将所选路径保存在资源文件夹中的文件中,这是我遇到的问题。 Reading and writing to this resource file will only need to be done when the program is actually running.仅在程序实际运行时才需要读取和写入此资源文件。 These read and write operations need to work for packaged jar files (I use maven) and in the IDE.这些读写操作需要在打包的 jar 文件(我使用 maven)和 IDE 中起作用。

I am able to read a resources file inside of the IDE and then save that file to the designated location specified in the file selector by doing this.我可以读取 IDE 内的资源文件,然后通过执行此操作将该文件保存到文件选择器中指定的指定位置。 However, I have not been able to do the same from a jar despite trying multiple other approaches from other threads.但是,尽管从其他线程尝试了多种其他方法,但我无法从 jar 执行相同的操作。

        ClassLoader classloader = Thread.currentThread().getContextClassLoader();
        InputStream is = classloader.getResourceAsStream("config.yml");
        try {
            if(is != null) {
                Files.copy(is, testFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
                is.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

So just to clarify when my project is loaded I need to listen for the user to select a valid path for files like my config.因此,为了澄清我的项目何时加载,我需要倾听用户 select 文件的有效路径,例如我的配置。 Then I want to write my config to that path which I can do from my IDE and is shown above but I cant figure this out when I compile my project into a jar file since I always receive a file not found error in my cmd.然后我想将我的配置写入我可以从 IDE 执行的路径,如上所示,但是当我将项目编译成 jar 文件时我无法弄清楚这一点,因为我总是在我的 ZDFFA20A7FA1A5FCDACC4C40Z 文件中收到一个文件未找到错误However, the main point of this post is so that I can figure out how to save that selected path to my resource folder to a file (it can be json, yml or whatever u like).但是,这篇文章的主要目的是让我可以弄清楚如何将选定的路径保存到我的资源文件夹到一个文件中(它可以是 json、yml 或任何你喜欢的)。 In the code above I was able to read a file but I have no idea how to go from that to get the files path since then reading and writing to it would be trivial.在上面的代码中,我能够读取一个文件,但我不知道如何从中获取文件路径 go ,因为那时读取和写入它是微不足道的。 Also keep in mind I need to be able to read and write to a resource folder from both my IDE and from a compiled jar.另外请记住,我需要能够从我的 IDE 和编译的 jar 中读取和写入资源文件夹。

The following code shows my attempt at reading a resource from a compiled jar.以下代码显示了我尝试从已编译的 jar 中读取资源。 When I added a print statement above name.startWith(path) I generated a massive list of classes that reference config.yml but I am not sure which one I need.当我在 name.startWith(path) 上方添加打印语句时,我生成了大量引用 config.yml 的类列表,但我不确定我需要哪一个。 I assume it has to be one of the paths relating to my project or possible the META-INF or META-INF/MANIFEST.MF path.我认为它必须是与我的项目相关的路径之一,或者可能是 META-INF 或 META-INF/MANIFEST.MF 路径。 Either way how am I able to copy the file or copy the contents of the file?无论哪种方式,我如何能够复制文件或复制文件的内容?

  final String path = "resources/config.yml";
  final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());

    if(jarFile.isFile()) {  // Run with JAR file
        try {
            final JarFile jar = new JarFile(jarFile);
            final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
            while(entries.hasMoreElements()) {
                final String name = entries.nextElement().getName();
                if (name.startsWith(path)) { //filter according to the path
                    System.out.println(name);
                }
            }
            jar.close();
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }

Also if you were wondering I got the above code from the following post and my first block of code I pasted above is actually in the else statement since the IDE code from that post also did not work.另外,如果您想知道我从以下帖子中获得了上述代码,而我在上面粘贴的第一个代码块实际上在 else 语句中,因为该帖子中的 IDE 代码也不起作用。 How can I access a folder inside of a resource folder from inside my jar File? 如何从 jar 文件中访问资源文件夹中的文件夹?

You can't write to files inside your JAR file, because they aren't actually files, they are ZIP entries.您不能写入 JAR 文件中的文件,因为它们实际上不是文件,它们是 ZIP 条目。

The easiest way to store configuration for a Java application is to use Preferences :为 Java 应用程序存储配置的最简单方法是使用Preferences

Preferences prefs = Preferences.userNodeForPackage(MyApp.class);

Now all you have to do is use any of the get methods to read, and put methods to write.现在您所要做的就是使用任何 get 方法来读取,并使用 put 方法来写入。

There is absolutely no need to write files into your resource folder inside a jar.绝对不需要将文件写入 jar 内的资源文件夹。 All you need to have is a smart classloader structure.您所需要的只是一个智能类加载器结构。 Either there is a classloader that allows changing jars (how difficult is that to implement?), or you just setup a classpath that contains an empty directory before listing all the involved jars.要么有一个允许更改 jars 的类加载器(实现起来有多难?),或者您只需在列出所有涉及的 jars 之前设置一个包含空目录的类路径。

As soon as you want to change a resource, just store a file in that directory.只要您想更改资源,只需将文件存储在该目录中即可。 When loading the resource next time, it will be searched on the classpath and the first match is returned - in that case your changed file.下次加载资源时,将在类路径上搜索它并返回第一个匹配项 - 在这种情况下,您更改的文件。

Now it could be that your application needs to be started with that modified classpath, and on top of that it needs to be aware which directory was placed first.现在,您的应用程序可能需要使用修改后的类路径启动,除此之外,它还需要知道首先放置了哪个目录。 You could still setup that classloader structure within a launcher application, which then transfers control to the real application loaded via the newly defined classloader.您仍然可以在启动器应用程序中设置该类加载器结构,然后将控制权转移到通过新定义的类加载器加载的真实应用程序。

You could also check on application startup whether a directory such as ${user.home}/${application_name}/data exists.您还可以在应用程序启动时检查${user.home}/${application_name}/data等目录是否存在。

If not, create it by extracting a predefined zip into this location.如果没有,请通过将预定义的 zip 提取到此位置来创建它。 Then just run your application which would load/write all the data in this directory.然后只需运行您的应用程序,它将加载/写入此目录中的所有数据。

No need to read/write to the classpath.无需读取/写入类路径。 No need to include 3rd party APIs.无需包含 3rd 方 API。 And modifying this initial data set would just mean to distribute a new zip to be extracted from.修改这个初始数据集只是意味着分发一个新的 zip 以从中提取。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM