Extracting FileList contents from drop event data

196 views
Skip to first unread message

Daniel Bachler

unread,
Nov 29, 2015, 11:02:49 AM11/29/15
to Elm Discuss
Hi!

I have finally come around to give Elm a real shot and I really enjoy it so far. As a first project, I am trying to create an image uploader in Elm. I got some elements working quickly but right now I am at a loss as to how to extract the File objects from the FileList object when the user drops files from his computer on the drop zone.

Here is what I got working so far, finding out how many files were dropped:

parseLength : Json.Decoder Int
parseLength = Json.at ["dataTransfer", "files"] <| oneOf
  [ Json.object1 identity ("length" := Json.int)
  , null 0
  ]

onDrop : Signal.Address Action -> Attribute
onDrop address = onWithOptions "drop" {stopPropagation = True, preventDefault = True}
parseLength (\vals -> Signal.message address (Drop vals))

I was hoping that I could extract the file objects inside the FileList similarly, like so:

parseFilenames : Json.Decoder (List String)
parseFilenames = Json.at ["dataTransfer", "files"] <|
  Json.list <| Json.object1 identity ("name" := Json.string)

Unfortunately this doesn't work. In native JS, the FileReader objects supports access in the form of files[0] but the elm json decoder doesn't like it because it's not actually a JS array. (at least that is how I understand the code in https://github.com/elm-lang/core/blob/3.0.0/src/Native/Json.js#L98 )

What are my options from here to get the filenames of the dropped files? Is there a way to augment the JSON decoder with custom native parsers? It would be pretty straightforward to turn a FileList into a JS array of File objects and then it should work fine if I understand everything correctly.

Or is there another way to get these values? Or do I have to pass the whole FileList into a port and convert it to a list of strings in native JS?

Any help would be appreciated!

Daniel

Daniel Bachler

unread,
Nov 30, 2015, 5:21:43 AM11/30/15
to Elm Discuss
Okay, I figured this one out. It turns out that decoding a FileReader as an object1 with the key "0" works fine to get the first File object. I.e.

parseFilename = Json.at ["dataTransfer", "files"] <|
  Json.object1 identity ("0" := (Json.object1 identity ("name" := Json.string)))

works just fine. It then took me a little while to figure out how to parse a List of filenames with the object keys set to 0 .. (length-1), but here is what I came up with in the end (I also decided to create a record that keeps the filename and the file blog itself as a Json.value so I can pass it to a port later on and feed that into a filereader.

type alias NativeFile =
  { name : String
  , blob : Value   
  }

parseFilenameAt : Int -> Json.Decoder NativeFile
parseFilenameAt index = Json.at ["dataTransfer", "files"] <|
  Json.object2 NativeFile ((toString index) := (Json.object1 identity ("name" := Json.string))) (toString index := Json.value)

parseFilenames : Int -> Json.Decoder (List NativeFile)
parseFilenames count =
 case count of
    0 ->
      succeed []
    _ ->
      Json.object2 (::) (parseFilenameAt (count - 1)) (parseFilenames (count - 1))

 
parseLength : Json.Decoder Int
parseLength = Json.at ["dataTransfer", "files"] <| oneOf
  [ Json.object1 identity ("length" := Json.int)
  , null 0
  ]

onDrop : Signal.Address Action -> Attribute
onDrop address = onWithOptions "drop" {stopPropagation = True, preventDefault = True} (parseLength `andThen` parseFilenames) (\vals -> Signal.message address (Drop vals))

Once I have managed to load this file and upload it I will try to write up all of it in a blog post.

Cheers,
Daniel

Simon

unread,
Nov 30, 2015, 9:35:04 AM11/30/15
to Elm Discuss
I wrote some native code for file uploading. The library is stuck on native code review, but you can hard code it into your dependencies if you wish
Simon

Daniel Bachler

unread,
Nov 30, 2015, 9:48:16 AM11/30/15
to Elm Discuss
Hi Simon, yes I saw it, thanks for mentioning it! I need to read images as a blob and I think your library ATM only reads text. I will use your library as a starting point and/or send you a pull request if you are interested in adding this functionality.

Daniel

Simon

unread,
Nov 30, 2015, 9:52:38 AM11/30/15
to Elm Discuss
Great - I would imagine it has more chance with native code review if it solve's more people's concerns
Simon
Reply all
Reply to author
Forward
0 new messages