Permalink
Browse files

Background thread to remove expired multipart tempfiles

  • Loading branch information...
1 parent 9a97af2 commit 5b413d7d94164b04ce081e70a0c90a625117449d @weavejester weavejester committed Jul 2, 2011
View
44 ring-core/src/ring/middleware/multipart_params/temp_file.clj
@@ -2,13 +2,41 @@
(:require [clojure.java.io :as io])
(:import java.io.File))
-(defn temp-file-store
- "Stores multipart file parameters as a temporary file."
- [item]
+(defmacro ^{:private true} do-every [delay & body]
+ `(future
+ (while true
+ (Thread/sleep (* ~delay 1000))
+ (try ~@body
+ (catch Exception ex#)))))
+
+(defn- expired? [file expiry-time]
+ (< (.lastModified file)
+ (- (System/currentTimeMillis)
+ (* expiry-time 1000))))
+
+(defn- remove-old-files [file-set expiry-time]
+ (doseq [file @file-set]
+ (when (expired? file expiry-time)
+ (.delete file)
+ (swap! file-set disj file))))
+
+(defn- make-temp-file [file-set]
(let [temp-file (File/createTempFile "ring-multipart-" nil)]
+ (swap! file-set conj temp-file)
(.deleteOnExit temp-file)
- (with-open [out (io/output-stream temp-file)]
- (io/copy (:stream item) out))
- (-> (select-keys item [:filename :content-type])
- (assoc :tempfile temp-file
- :size (.length temp-file)))))
+ temp-file))
+
+(defn temp-file-store
+ "Stores multipart file parameters as a temporary file."
+ ([] (temp-file-store {:expire-in 3600}))
+ ([{:keys [expires-in]}]
+ (fn [item]
+ (let [file-set (atom #{})
+ temp-file (make-temp-file file-set)]
+ (io/copy (:stream item) temp-file)
+ (when expires-in
+ (do-every expires-in
+ (remove-old-files file-set expires-in)))
+ (-> (select-keys item [:filename :content-type])
+ (assoc :tempfile temp-file
+ :size (.length temp-file)))))))
View
14 ring-core/test/ring/middleware/multipart_params/temp_file_test.clj
@@ -4,12 +4,24 @@
ring.middleware.multipart-params.temp-file))
(deftest test-temp-file-store
- (let [result (temp-file-store
+ (let [store (temp-file-store)
+ result (store
{:filename "foo.txt"
:content-type "text/plain"
:stream (string-input-stream "foo")})]
(is (= (:filename result) "foo.txt"))
(is (= (:content-type result) "text/plain"))
(is (= (:size result) 3))
(is (instance? java.io.File (:tempfile result)))
+ (is (.exists (:tempfile result)))
(is (= (slurp (:tempfile result)) "foo"))))
+
+(deftest test-temp-file-expiry
+ (let [store (temp-file-store {:expires-in 2})
+ result (store
+ {:filename "foo.txt"
+ :content-type "text/plain"
+ :stream (string-input-stream "foo")})]
+ (is (.exists (:tempfile result)))
+ (Thread/sleep 2500)
+ (is (not (.exists (:tempfile result))))))

0 comments on commit 5b413d7

Please sign in to comment.