Skip to content

Instantly share code, notes, and snippets.

@henryw374
Last active July 9, 2020 13:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save henryw374/1392de5fedcf540648b015c65c7fc4d3 to your computer and use it in GitHub Desktop.
Save henryw374/1392de5fedcf540648b015c65c7fc4d3 to your computer and use it in GitHub Desktop.
(ns ftp-client
(:require
[clojure.java.io :as io]
[clojure.tools.logging :as log]
[miner.ftp :as ftp])
(:import [java.nio.file Path Paths Files FileSystem FileSystems]
[java.nio.file.attribute FileAttribute]
[java.io File]))
(defn remove!
"Removes the contents under the given location.
Returns nil if an error was encountered during the operation, true otherwise."
[url location]
(try
(let [success? (ftp/with-ftp [client url]
(ftp/client-delete client location))]
(if success? true
(log/error "Could not remove:" url "/" location "!")))
(catch Exception e
(log/error e "Could not remove:" url "/" location "!"))))
(defn list-objects
"Accepts the glob arg filter out resources based on
the file name"
([url] (list-objects url "*"))
([url glob]
(ftp/with-ftp [client url]
(seq (mapv #(.getName %)
(.listFiles client glob))))))
(defn apply-stream-to
"Opens a stream to the content stored in ftp server and calls f with it.
throws if file not found, otherwise, returns the result of applying f"
[url location f]
(ftp/with-ftp [client url :file-type :binary]
(if-let [stream (ftp/client-get-stream client location)]
(with-open [input stream]
(let [result (f input)]
(ftp/client-complete-pending-command client)
result))
(throw
(java.io.FileNotFoundException.
(format "could not find %s at %s " location url))))))
(defn- dir-path-from-location [location]
(-> location
clojure.java.io/as-file
(.getParent)))
(defn- make-directory-structure [url dir-path]
(ftp/with-ftp [client url]
(let [dirs (.split dir-path "/")]
(loop [next-dir (first dirs)
remaining (rest dirs)]
(when-not (.changeWorkingDirectory client next-dir)
(if (.makeDirectory client next-dir)
(.changeWorkingDirectory client next-dir)
(throw (Exception. (str "couldnt make dir " next-dir)))))
(when (not-empty remaining)
(recur
(first remaining)
(rest remaining)))))))
(defn store-if-not-exists!
"Stores given input under the given location. Returns nil in case of failure.
input can either be a string that will work with clojure.java.io/as-file, or a java.io.InputStream
"
[url location input]
(try
(if (list-objects url location)
(log/info "Content under" url "/" location "already exists!")
(do (log/debug "Storing" input "in" url "/" location "...")
(ftp/with-ftp [client url
:file-type :binary]
(when-let [dir-path (dir-path-from-location location)]
(make-directory-structure url dir-path))
(let [input-stream (if (instance? java.io.InputStream input)
input
(java.io.FileInputStream. (io/as-file input)))
success? (with-open [instream input-stream]
(.storeFile client location instream))]
(if success? true
(log/error "Could not upload:" input " to " url "/" location "!"))))))
(catch Exception e
(log/error e "Could not upload:" input " to " url "/" location "!")
(remove! url location))))
(defn- escape [s]
(when s (java.net.URLEncoder/encode s "UTF-8")))
(defn- format-url [host port username password]
(let [username (escape username)
password (escape password)
host-port (if port (str host ":" port) host)
user-pass (if password (str username ":" password) username)]
(format "ftp://%s@%s" user-pass host-port)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment