2016-03-05 86 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。 –