In Elm code we listen for the change event of an <input type="file"> and decode the event payload into a Json.Decode.Value which we pass to this helper:
filePart : String -> Json.Decode.Value -> Http.Part
filePart name blob =
Native.Utils.FileReader.filePart name blob
If we wanted to migrate away from this solution to be compatible with 0.19, what would be the best approach?
You have an input element in your Elm code, the id of which you pass to JavaScript via a port function when an onChange event fires (file has been selected).
In your JS code, you use the FileReader API to read the contents of the input element as a data URL, which gives you access to the base64-encoded contents of the file.
You send the contents of the file back into Elm via a subscription port and do whatever you would like with it.
Let me know if you have any questions. One of my team members is using this approach for a feature that requires file uploading and it works without issue.
In all fairness, the scenario described here was not investigated by elm-ports-driver but my guess is that the same approach could be used to cover this scenario as well.
With this solution we would have to send the file as text. Our use-case actually requires sending the file up as binary data. (We are integrating with a GraphQL back-end and special behavior is triggered when binary data is present in the post body)
With this approach, you have the contents of the file as a base64 encoded string. If you need binary data, why not just decode the base64 string in Elm/JS before sending?
Another question: what’s your native FileReader helper doing under the hood? Isn’t it the same thing?
@xtian if you need binary data, you can simply read the file as an array buffer in the port code reader.readAsArrayBuffer(file) and send this array buffer directly in an http request (mdn doc of send). This isn’t elm code, since it’s JS over a port but still does the job.
Looks like there’s at least one library that will do it. Or if you don’t want to add a dependency, just use window.atob() in your port function before passing the value back into Elm, and decode it as whatever you need to (be it a Value or a String). Then you can use Http.stringPart to get the same Http.Part value you get back from your Native helper.
Either way, it may require a bit of massaging to get the data in the right format, but there’s no way that ports won’t work. It’s just a longer/type-safe way of calling the exact same JS function.
EDIT: Isn’t the code that you’re using something like this?
I can’t store binary data in a JS string, I need a blob representation.
No, I looked at that library for reference, but like I said: the only native code required for my use-case is in the original post. I can explain how it works more if you’d like.
The payload of the Http.Part is a File object. There is no type checking of that value at runtime. The only purpose of the native code is to side-step the type system because there is no blob representation in Elm.
I have investigated this and the main problem is the fact that elm-lang/http supports only stringPart. If support for Value parts is added, the same technique should work.