2015-06-16 18 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。 –