cljs-ajax icon indicating copy to clipboard operation
cljs-ajax copied to clipboard

Upload Progress Handler

Open hironroy opened this issue 7 years ago • 9 comments

Hi. Thanks for cljs-ajax!

What's the recommended way to wire up the goog.net.EventType.UPLOAD_PROGRESS event listening? When I've registered the handler on the returned xhrio request object:

(doto x 
  (.setProgressEventsEnabled true)
  (.listen goog.net.EventType.UPLOAD_PROGRESS progress-handler))

It does not fire. I believe it has to do with the fact that they're being registered after the send function has been triggered. I currently can register a triggering progress handler by overriding the AjaxImpl protocol with the following:

;; Overriding the default cljs-ajax behavior
;; In order to support :progress-handler
(extend-type goog.net.XhrIo
  AjaxImpl
  (-js-ajax-request
    [this
     {:keys [uri method body headers timeout with-credentials
             response-format progress-handler]
      :or {with-credentials false
           timeout 0}}
     handler]
    (when-let [response-type (:type response-format)]
      (.setResponseType this (name response-type)))
   ;; Check for the existence of a :progress-handler arg and register if it's there
    (when progress-handler
      (doto this (.setProgressEventsEnabled true)
            (.listen goog.net.EventType.UPLOAD_PROGRESS progress-handler)))
    (doto this
      (events/listen goog.net.EventType/COMPLETE
                     #(do (println "Overriden Complete")
                          (handler (.-target %))))
      (.setTimeoutInterval timeout)
      (.setWithCredentials with-credentials)
      (.send uri method body (clj->js headers)))))

;; Example call
(POST "upload-image"
                  {:body (doto (js/FormData.) (.append filename file))
                   :progress-handler #(println "Progress" %)
                   :handler #(println "Upload Image" %)
                   :error-handler #(println "Failed Upload" %)})

While that does work, I imagine that there's a cleaner way of registering for the upload-progress event that I'm missing. If not, I'd be happy to work on a PR to add support. Thanks!

hironroy avatar Mar 20 '17 13:03 hironroy

@JulianBirch I don't think that's currently possible, so a PR would be great. I could push out a new release with the change.

yogthos avatar Mar 20 '17 18:03 yogthos

I would love this functionality

acron0 avatar Mar 24 '17 17:03 acron0

You could write an interceptor to do this. And if you do, please send us the code!

Ideally, we'd like to make it work with all implementations. One of these days, XhrIo and XmlHttpRequest are both going to be old hat. :)

But don't worry too much about that. Even if it's not bulletproof, we can definitely put it somewhere.

JulianBirch avatar Jun 24 '17 17:06 JulianBirch

I encountered this exact issue today, I'd love to see a lightweight way to get progress events (at least in cljs) whilst POSTing.

Related: https://github.com/google/closure-library/blob/master/closure/goog/net/xhrio.js#L483

It looks like there's quite a bit of plumbing in place for this already. Since POST returns a goog.net.XhrIo instance already. Presumably, if this setProgressEventsEnabled were set, we could just listen to the object directly.

harold avatar Jun 28 '17 20:06 harold

Yeah, it shouldn't be too hard. I'm working on some other issues first, but if you want to hurry stuff along, I love pull requests.

JulianBirch avatar Jun 28 '17 21:06 JulianBirch

@hironroy Actually, what you're describing is pretty much exactly how I'd do it. The catch is that we'd need to document quite heavily that it only worked for XhrIo (the perils of having multiple implementations).

One of these days I need to deprecate the XhrIo implementation: it pulls a lot of code into your project for frankly unclear benefit.

JulianBirch avatar Jul 02 '17 12:07 JulianBirch

Hey guys,

I really wanted this feature so I implemented it in a fork. I am happy to submit a PR if you haven't started on this yet.

PR Preview https://github.com/JulianBirch/cljs-ajax/compare/master...RyanBertrand:master

I pushed to Clojars if you want to do a quick test. https://clojars.org/ryanbertrand/cljs-ajax

Doc :progress-handler - the handler function for progress events. This handler is only available when using the goog.net.XhrIo API

Example

(ajax/PUT url
          {:handler          (fn [data]
                               (println "Success"))
           :progress-handler (fn [e]
                               (println (str "Progress (" (.-loaded e) "/" (.-total e) ")  [" (* 100 (/ (.-loaded e) (.-total e))) "]%")))
           :format           :raw
           :body             js-file})

Thanks, R

RyanBertrand avatar Jul 10 '17 22:07 RyanBertrand

That looks excellent. Please do send it. Could I ask you to rebase onto the 0,7 branch?

I'll figure out how to do it for XmlHttpRequest. Java may have to wait.

JulianBirch avatar Jul 14 '17 10:07 JulianBirch

OK, I'm pushing sorting this out properly until 0.8. The existing code will go into 0.7, but it's very XhrIo specific and plain won't work with a Java implementation. I think we're going to need two handlers: upload-progress and download-progress. The callback methods should probably take two parameters: byte counts current and total. total will be null if we can't work it out on download.

There's a related question of the best way to hook into arbitrary events. Don't know the answer to this, or even if there is a good answer.

JulianBirch avatar Jul 30 '17 11:07 JulianBirch