使用clj ftp在Clojure中重用ftp连接
我正在尝试使用Clojure从FTP服务器获取文件。我想使用一个连接下载所有文件。我正在使用clj ftp进行此操作。不幸的是,我无法通过一个连接实现它。有两个功能:使用clj ftp在Clojure中重用ftp连接,clojure,ftp,Clojure,Ftp,我正在尝试使用Clojure从FTP服务器获取文件。我想使用一个连接下载所有文件。我正在使用clj 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
(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
时,一切正常。在尝试调用一个会话时,我得到了一个异常:NullPointerException org.apache.commons.net.SocketClient.getRemoteAddress(SocketClient.java:658)
我注意到在cljftp
中有很多类型提示,它有影响吗
整个堆栈跟踪
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)
我已经检查了ftp库的源代码,似乎您必须了解由
map
创建的延迟序列。否则,当从结果序列中提取元素时,在离开with ftp
块后执行对ftp/client get
的调用,此时创建的连接已经关闭
要解决此问题,您需要使用以下方法强制实现序列:
这将强制所有ftp/client get
调用在您的with ftp
范围内发生
另一方面,可能不希望同时实现所有序列,因为它可能会产生危险的后果(例如内存使用)。您可能会阅读更多关于Clojure lazy seqs的文章,其中包含了一些副作用
在特定情况下,
ftp/client get
返回boolean
值,指示文件下载到本地文件是否成功,因此这不是什么大问题。在其他情况下,您可能会重新设计API,以便您的函数不仅接受一系列文件,而且还接受一个函数,该函数封装了您希望对每个文件执行的操作,并将该函数应用于每个值,因为它一个接一个地被使用,而不会将整个序列保留在内存中@Frank Henard提出了一个有效的观点,您可以使用doseq
来实现这一点。您可以显示整个堆栈跟踪吗?我喜欢doseq
用于副作用循环。在这样的组合中是否也可以使用pmap
?我试着让它在2秒后挂起。还尝试了直接future
调用,但内存不足。这取决于您使用的API是否是线程安全的。在这种情况下,您需要检查FTPClient
是否可以从多个线程同时使用。
(defn one-session [files]
(ftp/with-ftp [client ftp-url]
(doall
(map #(ftp/client-get client %1)
files))))