我有以下项目 struct .

ProjectName
|
|---src
    |
    |---main
        |
        |---java
        |   |
        |   |---ModuleName
        |       |
        |       |---module-info.java
        |       |
        |       |---PackageName
        |           |
        |           |---Main.java
        |
        |---resources
            |
            |---ResourceParentFolder
                |
                |---ResourceSubFolderA
                |   |
                |   |---Resource1A.png
                |   |---Resource2A.png
                |   |---Resource3A.png
                |
                |---ResourceSubFolderB
                    |
                    |---Resource1B.png
                    |---Resource2B.png
                    |---Resource3B.png

这是我的Main.java美元.

package PackageName;

public class Main
{

   public static void main(String[] args) throws Exception
   {
   
      new Main();
   
   }

   public Main() throws Exception
   {
   
      System.out.println("0  - " + ModuleLayer.boot().findModule("ModuleName")     .map(o -> o.getClassLoader().getResource("PackageName/Main.class")));
      System.out.println("1  - " + ModuleLayer.boot().findModule("ModuleName")     .map(o -> o.getClassLoader().getResource("ResourceParentFolder")));
      System.out.println("2  - " + ModuleLayer.boot().findModule("ModuleName")     .map(o -> o.getClassLoader().getResource("ResourceParentFolder/ResourceSubFolderA")));
      System.out.println("3  - " + ModuleLayer.boot().findModule("ModuleName")     .map(o -> o.getClassLoader().getResource("/ResourceParentFolder")));
      System.out.println("4  - " + ModuleLayer.boot().findModule("ModuleName")     .map(o -> o.getClassLoader().getResource("/ResourceParentFolder/ResourceSubFolderA")));
      System.out.println("4b - " + ModuleLayer.boot().findModule("ModuleName")     .map(o -> o.getPackages()));
      System.out.println("5  - " + ModuleLayer.boot().modules());
      System.out.println("6  - " + ModuleLayer.boot().parents());
      System.out.println("7  - " + ModuleLayer.boot().toString());
      System.out.println("8  - " + Main.class.getClassLoader().getResource("ResourceParentFolder"));
      System.out.println("9  - " + Main.class.getClassLoader().getResource("/ResourceParentFolder"));
      System.out.println("10 - " + Main.class.getClassLoader().getResource("ResourceParentFolder/ResourceSubFolderA"));
      System.out.println("11 - " + Main.class.getClassLoader().getResource("/ResourceParentFolder/ResourceSubFolderA"));
      System.out.println("12 - " + Main.class.getClassLoader().getSystemClassLoader().getResource("ResourceParentFolder"));
      System.out.println("13 - " + Main.class.getClassLoader().getSystemClassLoader().getResource("/ResourceParentFolder"));
      System.out.println("14 - " + Main.class.getClassLoader().getSystemClassLoader().getResource("ResourceParentFolder/ResourceSubFolderA"));
      System.out.println("15 - " + Main.class.getClassLoader().getSystemClassLoader().getResource("/ResourceParentFolder/ResourceSubFolderA"));
   
   }

}
module ModuleName
{

   requires java.base;

}

下面是我的shell 脚本,它执行编译,执行它,创建一个模块化JAR,执行该JAR,然后调用jpackage.

echo "STARTING TO COMPILE MODULAR SOURCE CODE"

javac                                      \
        --module-source-path=src/main/java \
        --module=ModuleName            \
        -d classes

echo "STARTING TO RUN  MODULAR SOURCE CODE"

java                                             \
        --module-path="classes"                  \
        --module=ModuleName/PackageName.Main

echo "STARTING TO BUILD A MODULAR JAR"

jar                                               \
        --verbose                                 \
        --create                                  \
        --file run/executable/jar/ProjectName.jar \
        --main-class PackageName.Main             \
        -C classes/ModuleName .

echo "STARTING TO RUN  A MODULAR JAR"

java                                                      \
        --module-path="run/executable/jar"                \
        --module=ModuleName/PackageName.Main

echo "STARTING TO RUN JPACKAGE"

jpackage                                                  \
    --verbose                                             \
    --type msi                                            \
    --name ProjectName                                    \
    --input src/main/resources                            \
    --install-dir davidalayachew_applications/ProjectName \
    --vendor "David Alayachew"                            \
    --win-dir-chooser                                     \
    --module-path run/executable/jar                      \
    --module ModuleName/PackageName.Main                  \
    --win-console                                         \
    --java-options "--enable-preview"                     \
    --dest run/executable/installer

如您所见,它创建了一些目录.

现在,执行过程中的输出是我感到沮丧的地方.

以下是编译后立即运行时的输出.

STARTING TO RUN  MODULAR SOURCE CODE
0  - Optional[file:**ignore**/ProjectName/classes/ModuleName/PackageName/Main.class]
1  - Optional[file:**ignore**/ProjectName/src/main/resources/ResourceParentFolder]
2  - Optional[file:**ignore**/ProjectName/src/main/resources/ResourceParentFolder/ResourceSubFolderA]
3  - Optional.empty
4  - Optional.empty
4b - Optional[[PackageName]]
5  - [**ignore**]
6  - []
7  - **ignore**
8  - file:**ignore**/ProjectName/src/main/resources/ResourceParentFolder
9  - null
10 - file:**ignore**/ProjectName/src/main/resources/ResourceParentFolder/ResourceSubFolderA
11 - null
12 - file:**ignore**/ProjectName/src/main/resources/ResourceParentFolder
13 - null
14 - file:**ignore**/ProjectName/src/main/resources/ResourceParentFolder/ResourceSubFolderA
15 - null

好的,它找到了文件夹.因为它是java.net.URLConnection,所以我可以通过修改URL来遍历目录.因此,在编译之后立即运行代码就可以了.

以下是运行创建的JAR文件时的输出.

STARTING TO RUN  A MODULAR JAR
0  - Optional[jar:file:///**ignore**/ProjectName/run/executable/jar/ProjectName.jar!/PackageName/Main.class]
1  - Optional[jar:file:///**ignore**/ProjectName/run/executable/jar/ProjectName.jar!/ResourceParentFolder/]
2  - Optional[jar:file:///**ignore**/ProjectName/run/executable/jar/ProjectName.jar!/ResourceParentFolder/ResourceSubFolderA/]
3  - Optional.empty
4  - Optional.empty
4b - Optional[[ResourceParentFolder.ResourceSubFolderB, ResourceParentFolder.ResourceSubFolderA, ResourceParentFolder, PackageName]]
5  - [**ignore**]
6  - []
7  - **ignore**
8  - jar:file:///**ignore**/ProjectName/run/executable/jar/ProjectName.jar!/ResourceParentFolder/
9  - null
10 - jar:file:///**ignore**/ProjectName/run/executable/jar/ProjectName.jar!/ResourceParentFolder/ResourceSubFolderA/
11 - null
12 - jar:file:///**ignore**/ProjectName/run/executable/jar/ProjectName.jar!/ResourceParentFolder/
13 - null
14 - jar:file:///**ignore**/ProjectName/run/executable/jar/ProjectName.jar!/ResourceParentFolder/ResourceSubFolderA/
15 - null

好的,连接类型不同,但我们还是找到了相同的文件夹.我可能不得不做一些不同的URL建设,但前进的道路相当清晰.

现在,以下是我运行新安装的jpackage版应用程序后发生的事情.

0  - Optional[jrt:/ModuleName/PackageName/Main.class]
1  - Optional.empty
2  - Optional.empty
3  - Optional.empty
4  - Optional.empty
4b - Optional[[ResourceParentFolder, PackageName, ResourceParentFolder.ResourceSubFolderA, ResourceParentFolder.ResourceSubFolderB]]
5  - [module ModuleName, module java.base]
6  - []
7  - ModuleName, java.base
8  - null
9  - null
10 - null
11 - null
12 - null
13 - null
14 - null
15 - null

除了我作为健康判断放置的.class文件之外,什么也找不到.

所以,我打开了安装目录,我看到我的资源肯定在那里.整个目录、子目录和文件.我的树 struct 中的所有内容(包括ResourceParentFolder%)都已复制到C:/Program File/davidalayachew_applications/ProjectName/app文件夹中.

我try 了一系列配置,比如在APP中预先设置getResource个参数,但都没有效果.我已经循环了getResource个排列,老实说,我已经忘记了大部分.

有人能给我指点一下如何在调用jPackage后让我的应用程序访问我的资源吗?我特别需要能够看到文件夹的内容.而不仅仅是获取单个文件.

推荐答案

您不应该使用.getResource().getResourceAsStream()来创建目录或查看其内容.这从来不是他们设计的一部分.它们只用于获取文件,而不是扫描或查看目录的内容.

这就是我的臭虫的来源.是的,我可以简单地通过执行.getResource().getResourceAsStream()来查看文件夹内容,但这只适用于普通文件夹和JAR文件,因为普通文件夹(显然)具有内置的功能,而JAR可以在一定程度上模拟相同的功能.但试图对可执行文件(.exe文件)执行此操作显然是行不通的.以下是做这件事的真正方法.

模块是一件美好的事情.如果您想要读取模块的内容,只需获取一个java.lang.module.ModuleReader的实例.从那里,您可以获得我试图在getResource().getResourceAsStream()中获得的目录搜索功能.打ModuleReader.list()就行了.105我也在效仿@Slaw在这个答案中所做的事情.一百零二

package PackageName;

public class Main
{

   public static void main(String[] args) throws Exception
   {
   
      new Main();
   
   }

   public Main() throws Exception
   {
      var module = Main.class.getModule();
      var reference = module.getLayer()
              .configuration()
              .findModule(module.getName())
              .orElseThrow()
              .reference();
      try (var reader = reference.open()) {
          reader.list().forEach(System.out::println);
      }
   }

从那里,我可以扫描我的目录并查看我的模块的内容,我所需要做的就是使用ModuleReader.我可以将值传递到getResourcegetResourceAsStream调用中,它应该可以很好地工作.理想情况下,是模块特定的模块之一.

所以,让我们把这些新策略应用到我的问题上.我已经在这个答案中粘贴了@slaw‘S Main.java以上.这是我新的shell 脚本.

首先是原始的编译命令.对我来说,这一点没有改变.

javac                                      \
        --module-source-path=src/main/java \
        --module=ModuleName                \
        -d classes

接下来是运行模块化源代码.这一次需要稍加改动.

java                                                 \
        --module-path="classes"                      \
        --module=ModuleName/PackageName.Main         \
        --patch-module ModuleName=src/main/resources

如您所见,我在那里添加了一个--patch-module命令行.特别感谢(再次!)@Slaw的S回答--https://stackoverflow.com/a/77030323/--patch-module18965

这里有--patch-module命令的文档--https://docs.oracle.com/en/java/javase/20/docs/specs/man/java.html(Ctrl+F"--patch-模块")

此命令用于将资源插入到模块中.您所要做的就是在等号的左边指定您想要"修补"的模块,然后在右边递归地放置您想要复制的目录.

因此,在我的例子中,我希望将100 src/main/Resources(不是目录本身,而是其内容)复制到我的模块中.所以,我只是在执行Run命令时调用了上面的命令,情况很好!

我鼓励大家读一读@斯拉夫的S的回答,它更详细.

接下来是创建JAR文件的脚本.这一次也会略有变化.

jar                                               \
        --verbose                                 \
        --create                                  \
        --file run/executable/jar/ProjectName.jar \
        --main-class PackageName.Main             \
        -C classes/ModuleName .                   \
        -C src/main/resources .

唯一新增的是第二个-C电话.JAR命令没有--patch-module选项,但您可以将-C选项实质上视为--patch-module的别名.通过这样做,我达到了完全相同的效果(据我所知).

接下来是我对JAR执行的run命令.这一个保持不变.

java                                                      \
        --module-path="run/executable/jar"                \
        --module=ModuleName/PackageName.Main

之所以保持不变,是因为-C实际上将src/main/resources 102插入到罐子中.因此,不需要插入任何特殊命令!既然我们正在摄取罐子,那么我们也就摄取了它的所有内容--这意味着我们也获得了资源!

最后,是jPackageshell 脚本.这个实际上变得更简单了!

jpackage                                                  \
    --verbose                                             \
    --type msi                                            \
    --name ProjectName                                    \
    --install-dir davidalayachew_applications/ProjectName \
    --vendor "David Alayachew"                            \
    --win-dir-chooser                                     \
    --module-path run/executable/jar                      \
    --module ModuleName/PackageName.Main                  \
    --win-console                                         \
    --java-options "--enable-preview"                     \
    --dest run/executable/installer

这里唯一的变化是我从jPackage脚本中删除了--input选项.特别感谢@VGR指出这一点!--input现在是多余的,因为我们已经在JAR中拥有了资源--不需要手动插入它!

现在,当我运行上面的所有内容时,事情就完美地解决了!

以下是我运行模块化代码(而不是JAR)时的输出.

module-info.class
PackageName/
PackageName/Main.class
ResourceParentFolder/
ResourceParentFolder/ResourceSubFolderA/
ResourceParentFolder/ResourceSubFolderA/Resource1A.png
ResourceParentFolder/ResourceSubFolderA/Resource2A.png
ResourceParentFolder/ResourceSubFolderA/Resource3A.png
ResourceParentFolder/ResourceSubFolderB/
ResourceParentFolder/ResourceSubFolderB/Resource1B.png
ResourceParentFolder/ResourceSubFolderB/Resource2B.png
ResourceParentFolder/ResourceSubFolderB/Resource3B.png

请记住,我更改了输出命令,以简单地列出所有模块内容的位置.如果我想获取这些资源中的任何一个,我只会调用我的getResourcegetResourceAsStream个方法.

但为了绝对确定,我可以调用一个实际获取其中一个资源的方法.

这是我新的Main.java英镑.

package PackageName;
   
public class Main
{

   public static void main(String[] args) throws Exception
   {
   
      new Main();
   
   }

   public Main() throws Exception
   {
   
   
      try
      (
         final java.lang.module.ModuleReader reader =
            ModuleLayer
                .boot()
                .configuration()
                .findModule("ModuleName")
                .get()
                .reference()
                .open()
      )
      {
      
         final String firstResource =
            reader
               .list()
               .filter(each -> each.contains("ResourceParentFolder/ResourceSubFolderA/Resource1A.png"))
               .findFirst()
               .orElseThrow()
               ;
      
         final var myModule =
            ModuleLayer
               .boot()
               .findModule("ModuleName")
               .orElseThrow()
               ;
      
         final var inputStream = myModule.getResourceAsStream(firstResource);
      
         final java.awt.Image image = javax.imageio.ImageIO.read(inputStream);
      
         System.out.println(image);
      
      }
   
   
   
   }

}

现在,我不用打印所有资源,只需筛选出我想要的资源,然后加载它.

当我这样做的时候,一切都奏效了!

以下是我的run命令的输出(无JAR).

BufferedImage@3ecd23d9: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@569cfc36 transparency = 3 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 96 height = 88 #numDataElements 4 dataOff[0] = 3

它成功获取了图像!我可以将返回的对象放入我的图形用户界面中,一切都很正常!

以下是我的run命令(是的JAR)的输出.

BufferedImage@de0a01f: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@4c75cab9 transparency = 3 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 96 height = 88 #numDataElements 4 dataOff[0] = 3

1对1的比赛!

下面是我的jPackage安装的输出.

BufferedImage@1f554b06: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@694e1548 transparency = 3 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 96 height = 88 #numDataElements 4 dataOff[0] = 3

这是一场比赛!我现在有了一个查询模块并从中获取资源的解决方案!并且解决方案在所有执行上下文中都是统一的--无论我是在运行一些刚刚编译的.class文件,还是在运行JAR文件,或者是在将应用程序作为jPackage创建的可执行二进制文件执行,我都准备好了!

Java相关问答推荐

H2弹簧靴试验跌落台

替换com. sun. jndi. dns. DnsContextFactory Wildfly23 JDK 17

只需最少的代码更改即可将版本号标记添加到日志(log)

Spring Data JPA慢慢地创建了太多非活动会话

Java Mooc.fi Part 12_01.Hideout -返回和删除方法

如何为JavaFX Spring Boot应用程序制作Windows/MacOS/Linux安装程序

使用Jackson库反序列化json

有没有可能在时间范围内得到多种解决方案?

try 在两个不同数组的数字之间求平均值

如果第一位数字和最后一位数字相差超过一位,您将如何获得随机数?

如何在SWT菜单项文本中保留@字符

Java中的发布/订阅-Long Live和Short Live Publisher,哪种方法是正确的?

为什么JavaFX MediaPlayer音频播放在Windows和Mac上运行良好,但在Linux(POPOS/Ubuntu)上却有问题?

如何利用OpenTelemeter将初始值(零)输出到普罗米修斯

如何用Micrometer&;斯普肯

这是JavaFX SceneBuilder的错误吗?

将Optionals/null安全添加到嵌套的flatMap/流

如何转换Vector<;对象>;转换为int?

从 Java 17 切换回 Java 8 后出现的问题

在数组列表中找到对象后,未从数组中删除对象