2016-03-05 40 views
1

我試圖使用Clojure從FTP服務器獲取文件。我想用一個連接下載所有文件。我正在使用https://github.com/miner/clj-ftp/blob/master/src/miner/ftp.clj clj-ftp。不幸的是,我無法通過一個連接來實現它。有兩個功能:在Clojure中使用clj-ftp重新使用ftp連接

(defn one-session [files] 
    (ftp/with-ftp [client ftp-url] 
    (map #(ftp/client-get client %1) 
     files))) 

(defn get-all [files] 
    (map #(ftp/with-ftp [client ftp-url] 
      (ftp/client-get client %1)) 
     files)) 

當致電get-all一切工作正常。當試圖打電話one-session我得到了異常NullPointerException org.apache.commons.net.SocketClient.getRemoteAddress (SocketClient.java:658)

我注意到在clj-ftp有很多類型的提示,它有沒有影響嗎?

整個堆棧跟蹤

Exception in thread "main" java.lang.NullPointerException, compiling:(/private/var/folders/4d/77tz4xfj7b1dkqtd3h4j10v40000gn/T/form-init2973639134882885374.clj:1:125) 
    at clojure.lang.Compiler.load(Compiler.java:7391) 
    at clojure.lang.Compiler.loadFile(Compiler.java:7317) 
    at clojure.main$load_script.invokeStatic(main.clj:275) 
    at clojure.main$init_opt.invokeStatic(main.clj:277) 
    at clojure.main$init_opt.invoke(main.clj:277) 
    at clojure.main$initialize.invokeStatic(main.clj:308) 
    at clojure.main$null_opt.invokeStatic(main.clj:342) 
    at clojure.main$null_opt.invoke(main.clj:339) 
    at clojure.main$main.invokeStatic(main.clj:421) 
    at clojure.main$main.doInvoke(main.clj:384) 
    at clojure.lang.RestFn.invoke(RestFn.java:421) 
    at clojure.lang.Var.invoke(Var.java:383) 
    at clojure.lang.AFn.applyToHelper(AFn.java:156) 
    at clojure.lang.Var.applyTo(Var.java:700) 
    at clojure.main.main(main.java:37) 
    Caused by: java.lang.NullPointerException 
    at org.apache.commons.net.SocketClient.getRemoteAddress(SocketClient.java:658) 
    at org.apache.commons.net.ftp.FTPClient._openDataConnection_(FTPClient.java:789) 
    at org.apache.commons.net.ftp.FTPClient._retrieveFile(FTPClient.java:1854) 
    at org.apache.commons.net.ftp.FTPClient.retrieveFile(FTPClient.java:1845) 
    at miner.ftp$client_get.invokeStatic(ftp.clj:144) 
    at miner.ftp$client_get.invoke(ftp.clj:138) 
    at miner.ftp$client_get.invokeStatic(ftp.clj:140) 
    at miner.ftp$client_get.invoke(ftp.clj:138) 
    at zephyr.fetch$one_session$fn__1296.invoke(fetch.clj:30) 
    at clojure.core$map$fn__4785.invoke(core.clj:2644) 
    at clojure.lang.LazySeq.sval(LazySeq.java:40) 
    at clojure.lang.LazySeq.seq(LazySeq.java:49) 
    at clojure.lang.RT.seq(RT.java:521) 
    at clojure.core$seq__4357.invokeStatic(core.clj:137) 
    at clojure.core$print_sequential.invokeStatic(core_print.clj:46) 
    at clojure.core$fn__6072.invokeStatic(core_print.clj:153) 
    at clojure.core$fn__6072.invoke(core_print.clj:153) 
    at clojure.lang.MultiFn.invoke(MultiFn.java:233) 
    at clojure.core$pr_on.invokeStatic(core.clj:3572) 
    at clojure.core$pr.invokeStatic(core.clj:3575) 
    at clojure.core$pr.invoke(core.clj:3575) 
+0

你能展示整個堆棧跟蹤嗎? –

回答

3

我已檢查FTP庫的來源,似乎你必須認識到通過map創建的懶惰序列。否則,在從結果序列中提取元素時,在離開with-ftp塊後執行ftp/client-get的調用,並且此時創建的連接已經關閉。

要解決該問題,您需要強制使用doall序列的實現:

(defn one-session [files] 
    (ftp/with-ftp [client ftp-url] 
    (doall 
     (map #(ftp/client-get client %1) 
      files)))) 

這將迫使所有ftp/client-get轉接至您with-ftp範圍內發生。

另一方面,可能不希望一次實現所有的序列,因爲它可能有危險的後果(例如,內存利用率)。您可能會在Stuart Sierra's blog post上看到更多關於Clojure懶惰seqs與副作用混合的內容。

在你的特定情況下,ftp/client-get返回boolean值表示文件下載到本地文件是否成功,所以它不是一個大問題。在其他情況下,您可能會重新設計您的API,以便您的函數不僅接受一個seq文件,而且還包含一個函數,它封裝了您想要對每個文件執行的操作,並將該函數應用於每個值,因爲它是逐個消耗的,記憶中的序列。 @Frank Henard提出了一個有效的觀點,你可以使用doseq

+1

我喜歡'doseq'的副作用循環。 –

+0

在這樣的組合中是否也可以使用'pmap'?我試圖在2秒後掛起。也試過直接'未來'的電話,但我打了記憶。 – Sebastian

+1

這取決於您使用的API是否是線程安全的。在這種情況下,您需要檢查是否可以從多個線程同時使用FTPClient。 –