簡體   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