从外部clojar

时间:2015-06-16 02:11:06

标签: java maven clojure

我想要做的是在一个独立的Maven repo / clojar中打包一个大文件(MIDI soundfont),然后能够以编程方式将其拉下来并从一个单独的项目中使用它。这个看似简单的任务证明比我预期的要复杂得多。

如果有办法直接访问这些资源,或将它们公开为公共变量或其他东西,那么理想情况是什么。这是我尝试的第一件事 - 我做了类似的事情:

(ns midi.soundfont.fluid-r3
  (:require [clojure.java.io :as io]))

(def sf2
  (io/file (io/resource "fluid-r3.sf2")))

然而,我遇到的问题是io/resource只在当前类路径上找到资源文件。一旦我尝试从另一个项目(或从REPL)中要求这个命名空间,我得到:

java.lang.IllegalArgumentException: Not a file: jar:file:/Users/dave/.m2/repository/midi/soundfont/fluid-r3/midi.soundfont.fluid-r3/0.1.0/midi.soundfont.fluid-r3-0.1.0.jar!/fluid-r3.sf2

如果无法直接访问资源,我会很高兴解决方案涉及将文件复制到文件系统中的某个路径。我也尝试过这个,但是在尝试运行"将文件复制到文件系统时遇到了同样的问题"来自不同项目的方法 - io/resource仍然无法找到该文件,因为它不在当前类路径中。

我之前发现了类似的问题,例如:

但是,这些解决方案似乎只适用于复制当前(正在运行)项目中的资源文件。

可以做这两件事之一吗?

  1. 从外部clojar访问资源文件
  2. 将资源文件导入当前项目,以便我可以使用io/resource
  3. 访问它

3 个答案:

答案 0 :(得分:2)

正如dbasch正确解释的那样,io/resource返回一个URL,而不是一个文件。但是为什么你能用这个URL打开 REPL上的io/filelein run但不是来自jar?这是因为第一种情况下的URL指向普通文件 在文件系统中,当使用jar运行时,URL指向jar内的资源,因此它不是一个正确的文件。

我在this github repo做了一个例子。我将-main代码复制到此处以供参考:

(defn -main [& args]
  (let [r (io/resource "greet")]
    (println r)
    (println (slurp r))
    (with-open [rdr (io/reader r)]
      (println (clojure.string/join ", " (line-seq rdr))))
    (println (io/file r))))

使用lein run运行显示:

› lein run
#<URL file:/home/nicolas/projects/clojure/resources/resources/greet>
hello
world

hello, world
#<File /home/nicolas/projects/clojure/resources/resources/greet>

运行uberjar显示:

› java -jar target/resources-0.1.0-SNAPSHOT-standalone.jar 
#<URL jar:file:/home/nicolas/projects/clojure/resources/target/resources-0.1.0-SNAPSHOT-standalone.jar!/greet>
hello
world

hello, world
Exception in thread "main" java.lang.IllegalArgumentException: Not a file: jar:file:/home/nicolas/projects/clojure/resources/target/resources-0.1.0-SNAPSHOT-standalone.jar!/greet
        at clojure.java.io$fn__8588.invoke(io.clj:63)
        at clojure.java.io$fn__8572$G__8556__8577.invoke(io.clj:35)

查看#<URL file:/home/nico...#<URL jar:file:/home/nico...之间的区别,这解释了为什么您无法在其上调用(io/file),但您可以使用slurp阅读或创建有io/reader的读者。

答案 1 :(得分:1)

(io/resource "fluid-r3.sf2")是网址,不是文件。如果你想在内存中一次性使用它,你可以slurp,或者使用java.net.URL api将其作为流读取(如果你愿意,可以在阅读时将其写入文件)。 / p>

示例:

user> (type (clojure.java.io/resource "text.txt"))
java.net.URL
user> (slurp (clojure.java.io/resource "text.txt"))
"this is a text file\n"

答案 2 :(得分:0)

“从外部clojar访问资源文件”

我从this article找到了这个解决方案。德米特里·索特尼科夫(Dmitri Sotnikov)在他的冷冻剂项目中做到了这一点。

源代码为hereload-plugins函数将从外部clojar的每个资源目录下检索所有plugin.edn

(ns cryogen-core.plugins
  (:require [clojure.edn :as edn]
            [clojure.string :as string]
            [text-decoration.core :refer :all]))

(defn load-plugin [^java.net.URL url]
  (let [{:keys [description init]} (edn/read-string (slurp url))]
    (println (green (str "loading module: " description)))
    (-> init str (string/split #"/") first symbol require)
    ((resolve init))))

(defn load-plugins []
  (let [plugins (.getResources (ClassLoader/getSystemClassLoader) "plugin.edn")]
    (doseq [plugin (enumeration-seq plugins)]
      (load-plugin (. ^java.net.URL plugin openStream)))))

如果您不喜欢太多的Java互操作,则pomegranate library中有一个resources函数,该函数可以检索外部clojar中具有给定名称的所有资源。