Last active
August 29, 2015 14:23
-
-
Save ostronom/f4bece969e0fb8956ec8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns myns | |
(:require-macros [cljs.core.async.macros :refer [go-loop]]) | |
(:require [ajax.core :refer [ajax-request raw-response-format]])) | |
(def html5supported? | |
(not (or (undefined? js/File) | |
(undefined? js/Blob) | |
(undefined? js/FileList) | |
(not (or js/Blob.prototype.webkitSlice js/Blob.prototype.mozSlice js/Blob.prototype.slice false))))) | |
(def slicer | |
(if html5supported? | |
(cond | |
js/Blob.prototype.slice #(.slice %1 %2 %3) | |
js/Blob.prototype.mozSlice #(.mozSlice %1 %2 %3) | |
js/Blob.prototype.webkitSlice #(.webkitSlice %1 %2 %3)))) | |
(defn inject-data [form data] | |
(doseq [[k v] data] (.append form k v))) | |
(defprotocol IUploader | |
" Maybe, target must be target-fn to distinguish between classic/chunked endpoint. " | |
(upload [this target file form-data])) | |
(defrecord ClassicUploader [] | |
IUploader | |
(upload [_ target file form-data] | |
(let [res (chan) | |
params (doto (js/FormData.) (.append "file" file) (inject-data form-data)) | |
xhr (ajax-request {:method :post :params params :uri target :handler #(do (put! res [:complete]) (close! res))})] | |
(aset (.-upload xhr) "onprogress" #(put! res [:progress {:total (.-total %) :loaded (.-loaded %)}])) | |
res))) | |
(defn upload-chunk [target file form-data upload-id start-offset end-offset] | |
(let [res (chan) | |
size (.-size file) | |
params (doto (js/FormData.) | |
(inject-data form-data) | |
(.append "id" upload-id) | |
(.append "offset" start-offset) | |
(.append "total" size) | |
(.append "file" (slicer file start-offset end-offset (.-type file)) (.-name file)))] | |
(if (>= start-offset size) | |
(put! res :done) | |
(ajax-request {:uri target | |
:params params | |
:response-format (raw-response-format) | |
:method :post | |
:handler (fn [[ok result]] (put! res (if ok :done :error)))})) | |
res)) | |
(defrecord ChunkedUploader [chunk-size] | |
IUploader | |
(upload [_ target file form-data] | |
(let [upload-id (-> (js/Date.) .getTime str) | |
res (chan) | |
upload #(let [start (* chunk-size %) end (+ start chunk-size)] | |
(upload-chunk target file form-data upload-id start end)) | |
max-chunks (max 1 (js/Math.ceil (/ (.-size file) chunk-size)))] | |
(go-loop [chunk 0] | |
(when-let [e (<! (upload chunk))] | |
(when (and (= e :done) (= chunk max-chunks)) | |
(put! res [:complete]) | |
(close! res)) | |
(when (and (= e :done) (not= chunk max-chunks)) | |
(put! res [:progress {:loaded chunk :total max-chunks}]) | |
(recur (inc chunk))) | |
(when (= e :error) | |
(put! res [:error]) | |
(recur chunk)))) | |
res))) | |
(defn get-uploader [] | |
(if html5supported? (ChunkedUploader. (* 512 1024)) (ClassicUploader.))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment