2015-06-16 20 views
1

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

如果有一种方法可以直接访问这些资源,或者将它们公开为公共变量或什么的,那么最理想的是什么。这是我想的第一件事 - 我做了这样的事情:

(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仍然找不到文件,因为它不在当前类路径中。

我发现,已经被问过SO以前类似的问题,如:

然而,这些解决方案似乎只涉及到复制文件,它是一个资源在当前(运行)项目。

是否有可能做这两件事之一?

  1. 从外部clojar
  2. 导入资源文件访问的资源文件到当前项目,这样我就可以使用io/resource
+0

将资源复制到文件中是您的答案。如错误消息所述,jar内的资源不是文件。这不是类路径问题,它是一个基本事实,即jar中的资源不是文件。 – noisesmith

+0

“不是文件”似乎是Java告诉我无法从不存在的资源中创建文件的方式。在'midi.soundfont.fluid-r3'项目(其资源目录包含该文件)的类路径上运行时运行'(io/file(io/resource“fluid-r3.sf2”))',但会抛出在任何其他项目中运行时“不是文件”异常。 –

+0

为了澄清,我能够将资源复制到文件。问题是从一个不同的项目做到这一点。那有意义吗? –

回答

2

作为dbasch正确地解释的那样,io/resource返回一个URL,而不是一个文件。但是,为什么您可以在REPL或lein run上使用 io/file而不是从jar打开该URL?这是因为第一种情况下的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

(io/resource "fluid-r3.sf2")访问它是一个网址,而不是文件。如果您想一次将所有内容全部存入内存,您可以使用slurp,或者将其作为带有java.net.URL api的数据流进行读取(并在您阅读时将其写入文件中)。

实施例:

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" 
+0

我的问题不是如何复制文件,而是如何从另一个项目访问资源。如果我尝试访问的文件不在当前项目中,“io/resource”将失败,并显示“Not a file”异常。 –

+0

小修正:尝试在'(io/resource“fluid-r3.sf2”)上执行诸如'io/file','slurp'等操作''如果该文件不存在在当前资源路径上。 –

+1

如果它不在资源路径上,那么你的错误将会是IllegalArgumentException没有实现方法::make-reader of protocol:#'clojure.java.io/IOFactory找到类:nil clojure.core/-cache-protocol -fn(core_deftype.clj:554)' - 它不会提及任何有关文件的内容,因为没有关于slurp或资源的内容隐含文件,而'io/resource'对于不在路径上的资源返回nil。 –