繁体   English   中英

从 java 中的资源文件夹访问文件(运行 JavaScript 文件)

[英]Access a file from a resources folder in java (Run a JavaScript file)

我正在尝试从 java 项目运行 javascript 文件。 目前我正在使用 Nashorn java 脚本引擎来运行脚本和 maven 来编译和创建一个 jar 文件。

1)使用 Nashorn?

我收到警告:

Warning: Nashorn engine is planned to be removed from a future JDK release

我使用的是 openjdk 11 版本。这会是个大问题吗? 如果是这样,什么是我正在尝试做的最好的选择?

2)当它在 jar 文件中时,我无法让它工作

我得到一个 FileNotFoundException

Java.io.FileNotFoundException: /Script.js (No such file or directory)

我有一个 javascript 文件Script.js和两个java文件,包括我的主要方法的 App.java 和试图找到 javascript 文件的Process.java

src
 ┣ main
 ┃ ┣ java
 ┃ ┃ ┗ com
 ┃ ┃ ┃ ┗ group
 ┃ ┃ ┃ ┃ ┣ App.java
 ┃ ┃ ┃ ┃ ┗ Processor.java
 ┃ ┗ resources
 ┃ ┃ ┗ Script.js
 ┗ test
 ┃ ┗ java
 ┃ ┃ ┗ com
 ┃ ┃ ┃ ┗ group
 ┃ ┃ ┃ ┃ ┗ AppTest.java

App.java文件将构造一个处理器 object 并调用 function 连接。

public class App 
{
    public static void main( String[] args )
    {
        Processor p = new Processor();
        p.connect();
    }
}

进程.java

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import java.io.FileReader;
import java.io.FileNotFoundException;

public class Processor {

...

  /*LINK TO JAVASCRIPT*/
    public void connect(){
        // get instance of the JavaScript engine
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");
 
        // Load and execute the script
        try {
            engine.eval(new FileReader("/Script.js"));
        }
        catch (FileNotFoundException ex) {
            ex.printStackTrace();
        }
        catch (ScriptException ex) {
            ex.printStackTrace();
        }
    }
    
}

3)编辑:

我发现这可能有帮助:

getClass().getClassLoader().getResourceAsStream("/Script.js");

然而,这会返回一个 InputStream,是否有办法从 InputStream 运行 javascript 文件或将 InputStream 作为字符串获取?

使用 nashorn

我使用的是 OpenJDK 11 版本。这会是个大问题吗?

我会这么说。 JDK15 是第一个不包含 nashorn的 JDK。 最新的“长期支持版本”是 JDK17,当前的 OpenJDK 版本是 18,所以在这一点上,是的,这是一个大问题。

如果是这样,什么是我正在尝试做的最好的选择?

不要依赖 JDK 附带的 javascript 引擎,您应该选择第三方 javascript 引擎并将其与您的应用程序一起提供。 Java 应用程序应该有许多依赖项(也许有数百个)- JDK 不提供 web 模板引擎、web 路由、数据库交互库(JDBC 随 JDK 提供,但并不打算直接使用,它更低级库,JDBI、JOOQ 和 Hibernate 等构建于其上),等等。 有意为之——JDK永远不会破坏向后兼容性,这对许多类型的库来说都束手无策。 解决方案是使用强大且广泛的开源库。 每个流行的 java 构建工具(无论是 Maven、Gradle,还是 ant+ivy)都使它变得极其简单; 只需将你想要的库的 GAV (Group::Artifact::Version) ID 放入你的构建文件中,该工具就会处理所有事情(从镜像服务器网络下载,工具保持最新,运送它等)。

这是一个将GraalVM javascript 引擎添加到项目中的教程 它不是唯一的 javascript 引擎,您还有其他选择。 但它表明了原理。

当它在 jar 文件中时,我无法让它工作

new FileReader("/Script.js")

java中, File表示文件 jar 文件中的条目不是文件 因此, FileReader 你已经输了,你不能用那些东西来获取资源。

您想要使用的是 GRAS 机制:要求 java 的类加载器基础结构从它加载 class 文件的同一位置加载资源(如下所示:class 文件是资源- 您的 java 应用程序没有它们就无法工作,并且JVM 本身需要在您的应用程序运行时加载它们。为什么,比如说,您的 GUI 的图标文件,或者您想要通过 javascript 引擎抛出的一些 javascript 应该有什么不同?)

做到这一点的唯一方法是 GRAS。 它来自 2 forms:

表格1:当API取URL时object。

一些 API 有一个采用 URL 的方法。例如,ImageIcon 和 java 提供的 GUI 库中的大多数其他东西(swing、JavaFX 等)。 对于这些:

class Example {
  void example() {
    URL resource = Example.class.getResource("foo.png");
    new ImageIcon(resource);
  }
}

形式二:输入流

如果 API 不提供采用 URL 的方法变体,那么肯定有一个采用InputStream (用于基于字节的东西,如图像文件)或Reader (用于基于字符的东西,如 javascript)。 您可以通过指定字符集编码轻松地将输入流转换为阅读器:

try (InputStream in = ...) {
  Reader r = new InputStreamReader(in, StandardCharsets.UTF_8);
  somethingThatNeedsAReader(r);
}

鉴于这些是资源,您必须安全地关闭它们,因此需要 try-with-resources(或者如果必须的话,滚动您的 try/finally)。 否则,您的应用程序最终将开始崩溃,因为它用完了文件句柄。

所以,我们只需要知道如何获取 InputStream 对象——因为我们可以使用上面的技巧将它们变成读者。 它是这样工作的:

try (InputStream in = Example.class.getResourceAsStream("foo.js")) {
  ....
}

该路径字符串

您传递给getResourcegetResourceAsStream的字符串不完全是文件路径。 它看起来与Example.class (包含 Example 类代码的 class 文件)所在的位置相同,即使它位于 jar 文件中。 即使它是即时生成的,即时下载的——java 的类加载器系统是一个抽象概念,谁知道它是如何加载这些的。 这就是重点:您只需从任何可能的地方加载。

这样的条目有一个“根” - 你也可以从根开始 go ,通过在它前面加上斜杠。

例子:

  1. 您在 package com.foo 中有一个名为Examplecom.foo
  2. 和js文件一起打包成一个jar文件。
  3. 因此,在该 jar 文件中,有一个条目/com/foo/Example.class
  4. 假设 js 文件位于/com/foo/js/myapp.js

然后您可以使用以下任一方法将该资源加载为输入流:

Example.class.getResource("js/myapp.js"); // relative to com/foo
Example.class.getResource("/com/foo/js/myapp.js"); // relative to root

它不是文件系统 - 例如, ..不起作用。 如果它在/js/myapp.js中,那么 leading-slash-strategy 是唯一的选择。

糟糕的替代品

这些在教程和 SO 答案中很常见,但你不应该使用它们:

  1. getClass().getResource而不是Example.class.getResource - 如果有人创建子类,getClass 技巧就会失效。 这不太可能,但为什么要编写更糟糕的代码,对吧? 避免风格指南争论,并使用在更多情况下有效的版本。
  2. Example.class.getClassLoader().getResource() - 这不必要地更长,删除了使用相对路径的能力,并在某些情况下中断。 例如,作为引导加载加载的类没有类加载器,因此上面的代码将抛出NullPointerException 但是例如String.class.getResource("String.class")工作正常。

你不能做的事

资源加载器系统只有“加载资源”的“原语”。 没有list()操作! - 一些图书馆建议它存在,但这些是在某些情况下会破坏的黑客攻击。 因此,如果您想要类似“我可以在 js 目录中获取所有 js 文件的列表吗?”之类的东西。 答案很简单:不,那是不可能的。

对此的一般解决方案是制作一个文本文件,列出您感兴趣的资源,并将其保存到一个已知名称的文本文件中。 现在要“伪造”“列出所有资源”操作,您可以使用getResource加载该文件,对其进行处理,依次使用.getResource加载每个条目。 这正是 SPI 的工作方式(这就是 java 本身发现可用的 JDBC 驱动程序、字符集提供程序、加密提供程序等的方式)。 如果您需要“列出某种类型的所有资源”,现在您知道要搜索 web 的内容了:)

将 GraalVM 添加到 pom.xml

    <!-- https://mvnrepository.com/artifact/org.graalvm.js/js -->
        <dependency>
          <groupId>org.graalvm.js</groupId>
          <artifactId>js</artifactId>
          <version>22.0.0.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.graalvm.js/js-scriptengine -->
        <dependency>
          <groupId>org.graalvm.js</groupId>
          <artifactId>js-scriptengine</artifactId>
          <version>22.0.0</version>
        </dependency>

并更改了 Process.java 文件:

    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine graalEngine = manager.getEngineByName("graal.js");
    
    try (InputStream in = Process.class.getResourceAsStream("/Script.js")) {
       graalEngine.eval(new InputStreamReader(in, StandardCharsets.UTF_8));
    }

暂无
暂无

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

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