Java 惯用Clojure将资源从运行的jar复制到外部

Java 惯用Clojure将资源从运行的jar复制到外部,java,jar,clojure,io,copy,Java,Jar,Clojure,Io,Copy,这似乎是一个经典问题,但我找不到任何关于它的“clojure方法” 因此,我在参考资料/(leiningen项目)中有一个foo/目录。当jar'd/uberjar'd时,这个foo/目录放在jar的根目录下。由于jar中的文件在运行时可能在物理上不一致,所以不能使用基本复制函数将目录递归复制到外部世界 Java世界有几种解决方案(例如),但我没有找到Clojure的任何现有解决方案。 作为初学者(Clojure和Java),我不知道如何将上述解决方案翻译成Clojure。从Java逐行翻译成J

这似乎是一个经典问题,但我找不到任何关于它的“clojure方法”

因此,我在参考资料/(leiningen项目)中有一个foo/目录。当jar'd/uberjar'd时,这个foo/目录放在jar的根目录下。由于jar中的文件在运行时可能在物理上不一致,所以不能使用基本复制函数将目录递归复制到外部世界

Java世界有几种解决方案(例如),但我没有找到Clojure的任何现有解决方案。 作为初学者(Clojure和Java),我不知道如何将上述解决方案翻译成Clojure。从Java逐行翻译成Java互操作似乎并不正确。 有没有一种“官方”的、clojure惯用的方法来做到这一点


请注意,我使用的是Raynes的fs实用程序库。似乎没有一个函数可以直接实现这一点,但也许有一些元素可以用来简化这个过程?(除了明显的基本io sugar)

我不确定惯用部分,但资源目录中的文件可以这样列出

(defn list-resource
  [resource-dir]
  (let [resource (io/file (io/resource resource-dir))]
    (file-seq resource)))
将文件从资源中的目录foo复制到当前路径非常简单

(io/copy (io/file (io/resource "foo/test.txt")) (io/file "test.txt"))
将这两者结合起来,您应该能够编写一个递归函数来执行您的要求

几个月前我已经编写了这个函数,它将按URI列出类路径上的资源。然后,您可以尝试以下操作:

(require '[cpath-clj.core :as cp]
         '[clojure.java.io :as io])

(doseq [[path uris] (cp/resources (io/resource "foo"))
        :let [uri (first uris)
              relative-path (subs path 1)
              output-file (io/file output-directory relative-path)]]
  (with-open [in (io/input-stream uri)]
    (io/copy in output-file)))
由于编写库时没有考虑到这个用例,因此存在一些杂耍:

  • (cp/resources(io/resource“foo”)
    将为您提供
    foo
    目录的内容-如果您只使用了
    (cp/resources“foo”)
    则会在类路径上找到所有此类目录
  • 理论上,类路径上可以有多个具有相同路径的文件,这就是函数返回多个URI的原因;在我们的例子中,只有第一个是值得关注的
  • path
    总是以斜杠开头,所以要得到相对的,我们必须删除它

也许这对你有帮助。

在尝试了更多的方法并从#clojure那里得到了帮助后,我从上一个链接中找到了一个“类似于clojure-I-guess”的翻译:

(ns my-ns
  (:require [me.raynes.fs    :as f])
  (:import [java.util.jar JarFile JarEntry]))

(defn extract-dir-from-jar
  "Takes the string path of a jar, a dir name inside that jar and a destination
  dir, and copies the from dir to the to dir."
  [^String jar-dir from to]
  (let [jar (JarFile. jar-dir)]
    (doseq [^JarEntry file (enumeration-seq (.entries jar))]
      (if (.startsWith (.getName file) from)
        (let [f (f/file to (.getName file))]
          (if (.isDirectory file)
            (f/mkdir f)
            (do (f/mkdirs (f/parent f))
                (with-open [is (.getInputStream jar file)
                            os (io/output-stream f)]
                  (io/copy is os)))))))))

我想我可以稍微整理一下(尤其是在.startsWith位附近),但至少它工作得很好。

不幸的是,这就是全部要点:您不能在运行的jar中的文件上使用io/copy(在repl或lein run中工作),因为jar中的资源文件不处于“文件”状态(因此,如果在jar中的“文件”上使用io/file,则会抛出“IllegalArgumentException:not a file”(非文件)。它与lein run和REPL一起工作,因为资源文件不是从jar内部调用的,而是直接从文件系统调用的,因此它处于兼容的文件状态。您能提供一些有关为什么需要这样做的信息吗?我以前见过类似的情况,但这不是常见的用例,可能存在更简单的解决方案这是你想要做的!Hanks!现在还不能测试它,但这看起来确实很有希望,是我提出的更干净的替代方案!我认为这实际上符合库的范围,所以如果你找到一个你满意的解决方案,请随意打开一个PR,我很乐意合并它。:)您可以将
(枚举序列(.entries jar))
替换为
(filter#(重新匹配自(.getName%))(枚举序列(.entries jar))
,并将
from
参数设置为正则表达式,从而使其更加灵活。然后可以删除
(如果(.startsWith…)
(defn copy-resource! [resource-filename]
  (let [resource-file (io/resource resource-filename)
        tmp-file (io/file "/tmp" resource-filename)]
    (with-open [in (io/input-stream resource-file)] (io/copy in tmp-file))))

(copy-resource! "my-logo.png")