Cloudinary uploads direct from Elm

rwoodnz profile image Richard Wood ・3 min read

If you need to store your images on a third party server, you may well have considered Cloudinary - https://cloudinary.com/ . It has a raft of cool image management features that take away a lot of the hassles of dealing with images in your application.

Cloudinary has support for a variety of languages but as anticipated nothing explicit for Elm.

But no problem as there is elm-cloudinary - http://package.elm-lang.org/packages/ghivert/elm-cloudinary/latest

And the good news is it appears to work out-of-the-box. There is some JS code you need to put into your index.js and then follow the example to include the necessary fields, msgs and update handling.

Now, I didn't actually have it working at that point, since our requirement is to upload unsigned uploads direct from the browser. This is a feature that Cloudinary supports but it looks like it came out after elm-cloudinary being released.

So, time to hack the cloudinary.elm file. What I discovered was that simply a different settings file was needed.

The existing settings file is:

type alias Settings =
    { username : String
    , apiKey : String
    , timestamp : Int
    , signature : String

For unsigned calls these need to be replaced with:

type alias SettingsUnsigned =
    { username : String
    , uploadPreset : String

You get the uploadPreset string from the Cloudinary management portal.

Rather than have one overall settings record and fill in different fields, the settings have to change because if we continue to include the authorisation fields in the original settings then Cloudinary won't accept the upload.

What I did is take each of the features - uploadPhoto and trackUploadPhoto - and create unsigned alternatives using the unsigned settings. This is a cheap approach that means I haven't disrupted the signed features if I need to use them later. Intention will be that when I am confident this is working I'll file a pull request back to the original elm-cloudinary author.

I have tried experimenting with whether union types or some other construct might provide for the original function set and just vary the settings. Haven't been able to do that yet without breaking changes to the API. Keen to hear if anyone has ideas for this.

Attempt to track progress

In the meantime the other obstacle I came across was in the trackUploadPhoto feature, or in my case I created trackUploadPhotoUnsigned. This arose because we were uploading a Cloudinary image but without a progress indicator.

The elm-cloudinary uses Elm's Http.Progress and looking in the console at the network responses showed that the Progress.track subscription was only responding that it is done, but not sending 'Some' notifications along the way.

Exploring this I found the following very helpful post request example: https://github.com/gdotdesign/elm-ui-examples/blob/master/file-upload-progress/source/Main.elm

I tried implementing a bit of this, even making my own custom request and to cut a long story short I found that if I sent the file to a dead drop such as "https://httpbin.org/post" instead of Cloudinary's url then I got back the Progress Some notifications.

Further seeking help online I have so far concluded that this is to do in the Cloudinary case with the request using 'chunking' in transfer-encoding, which results in content-length not being returned in the request response. Without that, progress cannot be determined.

Cloudinary provides progress capability in its JS widgets and I've asked them if there is another way to get this information. Any suggestions in this area are welcome as I'm clearly still learning about the intricacies of http requests and responses.


Editor guide