如何在Clojure中加载程序资源

时间:2023-01-24 16:20:31

How do you load program resources such as icons, strings, graphical elements, scripts, and so on in a Clojure program? I am using a project layout similar to that in many Java projects where there is a "resources" directory hanging off of a "source" directory. A jar file is created from the source and includes the resources, but I can't seem to get the resources loaded as I would in Java.

如何在Clojure程序中加载程序资源,如图标,字符串,图形元素,脚本等?我使用的项目布局类似于许多Java项目中的项目布局,其中“资源”目录悬挂在“源”目录之外。从源创建一个jar文件并包含资源,但我似乎无法像在Java中那样加载资源。

The first thing I tried was something like

我尝试的第一件事就像是

(ClassLoader/getSystemResource "resources/myscript.js")

But could never find the resource.

但永远找不到资源。

You can do something similar with

你可以做类似的事情

...
  (let [cls (.getClass net.mydomain.somenamespace)
        strm (.getResourceAsStream cls name)        ]
...

where name is the name of the resource to load, but the stream is nil.

其中name是要加载的资源的名称,但流是nil。

You can try using the context class loader with something like

您可以尝试使用类似的上下文类加载器

...

(let [thr (Thread/currentThread)
      ldr (.getContextClassLoader thr)
      strem (.getResourceAsStream ldr name)]
...

But strem is always nil.

但是,strem总是零。

In frustration, I've tried placing the resource files in just about every directory in the program. They get copied into the jar correctly, but I still can't seem to load them.

令人沮丧的是,我尝试将资源文件放在程序的几乎每个目录中。它们被正确地复制到jar中,但我似乎仍然无法加载它们。

I've looked at the language sources for the load function and the run-time library, but am not "getting" it.

我已经查看了加载函数和运行时库的语言来源,但我没有“得到”它。

Any help would be appreciated.

任何帮助,将不胜感激。

EDIT: Here's a more concrete example. In Java, if you wanted to convert MarkDown to HTML, you might use the showdown.js script and write something like:

编辑:这是一个更具体的例子。在Java中,如果要将MarkDown转换为HTML,可以使用showdown.js脚本并编写如下内容:

package scriptingtest;

import java.io.InputStreamReader;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class Example {

    private Object converter;

    public String transformMarkDown(String markdownString) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("js");
        try {
            engine.eval(new InputStreamReader(getClass().getResourceAsStream(
                    "resources/showdown.js")));
            converter = engine.eval("new Showdown.converter()");
        } catch (Exception e) {
            return "Failed to create converter";
        }
        try {
            return ((Invocable) engine).invokeMethod(converter, "makeHtml",
                    markdownString).toString();
        } catch (Exception e) {
            return "Conversion failed";
        }
    }

    public static void main(String[] args) {
        System.out.println(new Example().transformMarkDown("plain, *emphasis*, **strong**"));
    }
}

when I create the project, it all gets compiled and packed into a jar. When run, the program outputs <p>plain, <em>emphasis</em>, <strong>strong</strong></p>

当我创建项目时,它都被编译并打包到jar中。运行时,程序输出

plain, emphasis , strong

A literal translation to Clojure seems pretty straightforward, but I run into trouble trying to create the InputStreamReader -- I can't seem to write the code needed to find the script file in the jar.

对Clojure的字面翻译似乎非常简单,但是我在尝试创建InputStreamReader时遇到了麻烦 - 我似乎无法编写在jar中查找脚本文件所需的代码。

Edit: Added "markdown" tag since the post gives two complete examples of approaches to processing markdown.

编辑:添加了“markdown”标签,因为该帖子提供了两个处理降价处理方法的完整示例。

4 个解决方案

#1


49  

(clojure.java.io/resource "myscript.js")

#2


5  

It's the directory structure.

这是目录结构。

Continuing with the scripting engine example in the OP, a Clojure equivalent would be:

继续OP中的脚本引擎示例,Clojure等价物将是:

(ns com.domain.example
  (:gen-class)
  (:import (java.io InputStreamReader))
  (:import (javax.script ScriptEngineManager ScriptEngine)))

(defn load-resource
  [name]
  (let [rsc-name (str "com/domain/resources/" name)
        thr (Thread/currentThread)
        ldr (.getContextClassLoader thr)]
    (.getResourceAsStream ldr rsc-name)))

(defn markdown-to-html
  [mkdn]
  (let [manager (new ScriptEngineManager)
        engine (.getEngineByName manager "js")
        is (InputStreamReader. (load-resource "showdown.js"))
        _ (.eval engine is)
        cnv-arg (str "new Showdown.converter().makeHtml(\"" mkdn "\")")]
    (.eval engine cnv-arg)))

(defn -main
  []
  (println (markdown-to-html "plain, *emphasis*, **strong**")))

Note that the path to the resources is com/domain/resources for this code as opposed to com/domain/scriptingtest/resources in the Java version. In the clojure version, the source file, example.clj is in com/domain. In the Java version, the source file, Example.java is in the com/domain/scriptingtest package.

请注意,资源的路径是此代码的com / domain / resources,而不是Java版本中的com / domain / scriptingtest / resources。在clojure版本中,源文件example.clj位于com / domain中。在Java版本中,源文件Example.java位于com / domain / scriptingtest包中。

When setting up a project in my IDE, NetBeans, the Java project wizard asks for an enclosing package for the source. The Clojure plugin, enclojure, asks for a namespace, not a package. I had never noted that difference before. Hence the "off-by-one" error in the directory structure expected.

在我的IDE NetBeans中设置项目时,Java项目向导会要求提供源的封装包。 Clojure插件,包装,请求命名空间,而不是包。我之前从未注意到这种差异。因此,期望的目录结构中的“off-by-one”错误。

#3


4  

you can also use clojure.lang.RT/baseLoader

你也可以使用clojure.lang.RT / baseLoader

(defn serve-public-resource [path]
  (.getResourceAsStream (clojure.lang.RT/baseLoader) (str "public/" path))) 

#4


3  

I placed the file in testpkg/test.txt (relative to current directory).

我将文件放在testpkg / test.txt中(相对于当前目录)。

Code:

(def x 5)
(def nm "testpkg/test.txt")
(def thr (Thread/currentThread))
(def ldr (.getContextClassLoader thr))
(def strem (.getResourceAsStream ldr nm))
(def strem2 (ClassLoader/getSystemResource nm))
(. System/out (println "First Approach:"))
(. System/out (println strem))
(. System/out (println))
(. System/out (println))
(. System/out (println "Second Approach:"))
(. System/out (println strem2))

$ java -cp .\;clojure.jar clojure.main test.clj

$ java -cp。\; clojure.jar clojure.main test.clj

First Approach: java.io.BufferedInputStream@1549f94

第一种方法:java.io.BufferedInputStream@1549f94

Second Approach: file:/C:/jsight/javadevtools/clojure-1.1.0/testpkg/test.txt

第二种方法:文件:/ C:/jsight/javadevtools/clojure-1.1.0/testpkg/test.txt

#1


49  

(clojure.java.io/resource "myscript.js")

#2


5  

It's the directory structure.

这是目录结构。

Continuing with the scripting engine example in the OP, a Clojure equivalent would be:

继续OP中的脚本引擎示例,Clojure等价物将是:

(ns com.domain.example
  (:gen-class)
  (:import (java.io InputStreamReader))
  (:import (javax.script ScriptEngineManager ScriptEngine)))

(defn load-resource
  [name]
  (let [rsc-name (str "com/domain/resources/" name)
        thr (Thread/currentThread)
        ldr (.getContextClassLoader thr)]
    (.getResourceAsStream ldr rsc-name)))

(defn markdown-to-html
  [mkdn]
  (let [manager (new ScriptEngineManager)
        engine (.getEngineByName manager "js")
        is (InputStreamReader. (load-resource "showdown.js"))
        _ (.eval engine is)
        cnv-arg (str "new Showdown.converter().makeHtml(\"" mkdn "\")")]
    (.eval engine cnv-arg)))

(defn -main
  []
  (println (markdown-to-html "plain, *emphasis*, **strong**")))

Note that the path to the resources is com/domain/resources for this code as opposed to com/domain/scriptingtest/resources in the Java version. In the clojure version, the source file, example.clj is in com/domain. In the Java version, the source file, Example.java is in the com/domain/scriptingtest package.

请注意,资源的路径是此代码的com / domain / resources,而不是Java版本中的com / domain / scriptingtest / resources。在clojure版本中,源文件example.clj位于com / domain中。在Java版本中,源文件Example.java位于com / domain / scriptingtest包中。

When setting up a project in my IDE, NetBeans, the Java project wizard asks for an enclosing package for the source. The Clojure plugin, enclojure, asks for a namespace, not a package. I had never noted that difference before. Hence the "off-by-one" error in the directory structure expected.

在我的IDE NetBeans中设置项目时,Java项目向导会要求提供源的封装包。 Clojure插件,包装,请求命名空间,而不是包。我之前从未注意到这种差异。因此,期望的目录结构中的“off-by-one”错误。

#3


4  

you can also use clojure.lang.RT/baseLoader

你也可以使用clojure.lang.RT / baseLoader

(defn serve-public-resource [path]
  (.getResourceAsStream (clojure.lang.RT/baseLoader) (str "public/" path))) 

#4


3  

I placed the file in testpkg/test.txt (relative to current directory).

我将文件放在testpkg / test.txt中(相对于当前目录)。

Code:

(def x 5)
(def nm "testpkg/test.txt")
(def thr (Thread/currentThread))
(def ldr (.getContextClassLoader thr))
(def strem (.getResourceAsStream ldr nm))
(def strem2 (ClassLoader/getSystemResource nm))
(. System/out (println "First Approach:"))
(. System/out (println strem))
(. System/out (println))
(. System/out (println))
(. System/out (println "Second Approach:"))
(. System/out (println strem2))

$ java -cp .\;clojure.jar clojure.main test.clj

$ java -cp。\; clojure.jar clojure.main test.clj

First Approach: java.io.BufferedInputStream@1549f94

第一种方法:java.io.BufferedInputStream@1549f94

Second Approach: file:/C:/jsight/javadevtools/clojure-1.1.0/testpkg/test.txt

第二种方法:文件:/ C:/jsight/javadevtools/clojure-1.1.0/testpkg/test.txt