How to execute and handle file-uploads with Re-frame and Reitit?

We have an application using re-frame, re-frame-http-fx, and reitit on the backend. At length we were finally able to get the upload to attach an file to some FormData, but we are having a hard time receiving it on the back-end and saving it to disk. All the examples show a [:params :file] coming in, but we see nothing at all with :params. This is an embarrassingly standard process; does anyone have examples of how do we do it?

What you receive on your backend depends not only on what you send but also on what you used to write your backend. E.g. in Yada it’s (-> ctx :parameters :form %parameter-key%).

But to answer your question in a more universal way - you should be able to simply inspect the request object and figure out where the relevant data is. If you don’t know how to do it, it’s paramount to learn it. A common and IDE-independent approach is to use tap> along with GitHub - djblue/portal: A clojure tool to navigate through your data. or GitHub - vlaaad/reveal: Read Eval Visualize Loop for Clojure.

2 Likes

The default request just contains the bare minimum so you need to opt-in to enable any extras, however “standard” the process might be. The way to do that in Ring and by extension Reitit is to use middleware (though I believe Reitit also supports interceptors, which I’m not familiar with at all).

From memory I think all you need to enable parameters is to add with-params in Ring, or in Reitit there is a wrapper which you can use called parameters-middleware which you can find in:

[reitit.ring.middleware.parameters
    :refer (parameters-middleware)]

and add it to the route in question, e.g.

["/route" 
 {:middleware [parameters-middleware]
  :post (fn [req] )
}]

That should at least enable the :params key, as well as I think :query-params and :form-params.

After that I think you then need to add wrap-multipart-params, which you can find in the ring-core package, documentation here: https://ring-clojure.github.io/ring/ring.middleware.multipart-params.html

I did this a while ago, but can’t find the example code, so the above are only suggestions based on what I can dig up from code I do have coupled with what I remember.

1 Like

I just learned about Tap, and it is indeed awesome. However, I already have a habit of inspecting a complete request via the repl when necessary, and the file/temp file/resource-stream is not there. I think my middleware may be misconfigured to read it.

As a status update, I now have the data apparently attached like this:

:body #object[io.undertow.io.UndertowInputStream 0x68721850 "io.undertow.io.UndertowInputStream@68721850"

This isn’t yet usable, but it seems like a step in the right direction

I got it. Thanks for the advice that led me to multipart params. That was part of the story. I wish I could say it was easy. Maybe this will save someone else trouble in the future Uploading Files and Handling Upload Requests in Clojure[script] | Tech.ToryAnderson.com

Thanks for taking the time to write this up. Every time I get stuck on something in Clojure and find the solution on a blog post I’m grateful for the time that someone took write things down and save me and others time.