我想要做的是在一个独立的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
仍然无法找到该文件,因为它不在当前类路径中。
我之前发现了类似的问题,例如:
但是,这些解决方案似乎只适用于复制当前(正在运行)项目中的资源文件。
可以做这两件事之一吗?
io/resource
答案 0 :(得分:2)
正如dbasch正确解释的那样,io/resource
返回一个URL,而不是一个文件。但是为什么你能用这个URL打开
REPL上的io/file
或lein 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)在他的冷冻剂项目中做到了这一点。
源代码为here。 load-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中具有给定名称的所有资源。