Idiomatic Clojure to copy resources from running jar to outside

clojure, copy, io, jar, java

Solution

I've written cpath-clj a few months back that will list resources on the classpath by URI. You could then try the following:

(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)))

There is some juggling going on since the library was not written with this use case in mind:

- `(cp/resources (io/resource "foo"))` will give you the contents of your `foo` directory - if you had only used `(cp/resources "foo")` all such directories on the classpath would have been found,

- theoretically, there can be multiple files with the same `path` on the classpath, that's why the function returns multiple `uris`; in our case, only the first one is of interest.

- `path` is always starting with a slash, so to get the relative one, we have to remove it.

Maybe that's helpful to you.

Problem

It seems like a classical problem but I can't find anything about it "the clojure way". So, I have a foo/ directory inside resources/ (leiningen project). When jar'd/uberjar'd, this foo/ directory is put at the root of the jar. As files inside jar may not be physically consistent at runtime, you can't use basic copy function to recursively copy the directory to the outside world. Several solutions for the Java world exist (How to write a Java program which can extract a JAR file and store its data in specified directory (location)? and How to extract directory (and sub directories) from jar resource? for example), but I didn't find any existing solution for Clojure. As a beginner (both Clojure and Java), I'm not sure how to translate above solutions to Clojure. Translating literally line by line from Java to Clojurish Java Interop doesn't seem right. Is there an "official", clojure-idiomatic way to do this ? Note that I'm using Raynes' fs utilities library. There doesn't seem to be a function to do this directly, but maybe there's some elements I can use to simplify the process ? (besides the obvious basic io sugar)

Original source

Related problems