Peter
Peter

Reputation: 48958

How to zip a folder recursively with Clojure

I would like to zip a folder recursevely with clojure. An example of such a folder would be

├── a
├── b
│   ├── c
│   │   └── ccc.txt
│   └── bb.txt
├── c
├── a.txt
└── b.txt

Option 1 : using the OS from within Clojure

What works with zip in Ubuntu is to execute following in the root of this structure :

zip -r result.zip *

But you have to be in the working dir in order to do this. Using absolute paths will yield other results and omiting paths all together will flatten the structure of course.

Problem is that you cannot change the working directory in Clojure, not that I'm aware of that is..

Option 2 : using native Clojure (or Java)

This should be possible, and I find some zip implementations in Clojure or Java wrappers. Most of them however for single files.

This might be a solution : http://www.java-forums.org/blogs/java-io/973-how-work-zip-files-java.html

but before I try that out I'd like to now or there isn't a good Clojure library around.

Upvotes: 3

Views: 1330

Answers (4)

Nick Volk
Nick Volk

Reputation: 21

You can use rtcritical/clj-ant-tasks library that wraps Apache Ant, and zip/unzip in a single line of code.

Add library dependency [rtcritical/clj-ant-tasks "1.0.1"]

(require '[rtcritical.clj-ant-tasks :refer [run-ant-task])

To zip a directory:

(run-ant-task :zip {:destfile "/tmp/archive.zip" :basedir "/tmp/archive"})

Zip a directory, where the base directory is included in the archive:

(run-ant-task :zip {:destfile "/tmp/archive.zip" 
                    :basedir "/tmp" 
                    :includes "archive/**"})

Note: run-ant-task(s) functions in this library namespace can be used to run any other Apache Ant task(s) as well.

For more information, see https://github.com/rtcritical/clj-ant-tasks

Upvotes: 2

nha
nha

Reputation: 18005

Based on @Kyle answer:

(require '[clojure.java.io :as io])
(import '[java.util.zip ZipEntry ZipOutputStream])


(defn zip-folder
  "p input path, z output zip"
  [p z]
  (with-open [zip (ZipOutputStream. (io/output-stream z))]
    (doseq [f (file-seq (io/file p)) :when (.isFile f)]
      (.putNextEntry zip (ZipEntry. (str/replace-first (.getPath f) p "") ))
      (io/copy f zip)
      (.closeEntry zip)))
  (io/file z))

Upvotes: 0

Kyle
Kyle

Reputation: 22258

What about something like this using clojure.core/file-seq

(require '[clojure.java.io :as io])
(import '[java.util.zip ZipEntry ZipOutputStream])

(with-open [zip (ZipOutputStream. (io/output-stream "foo.zip"))]
  (doseq [f (file-seq (io/file "/path/to/directory")) :when (.isFile f)]
    (.putNextEntry zip (ZipEntry. (.getPath f)))
    (io/copy f zip)
    (.closeEntry zip)))

Upvotes: 8

Jaime Agudo
Jaime Agudo

Reputation: 8286

Check https://github.com/AeroNotix/swindon A nice little wrapper around java.util.zip. using streams and https://github.com/chmllr/zeus a Simple Clojure library for zip-based compression

Upvotes: 0

Related Questions