8

I am trying to upload a file using Fable-Elmish and the React Helpers. However, I can't work out how to convert the form event when a file is selected into something that I can send off to the server using Fetch. This is the view:

R.input [
        ClassName "input-control" 
        Type "file"
        OnChange (fun x -> FileUploaded x.target |> dispatch )
    ] []

The corresponding part of my update function:

 | FileUploaded file ->
    model, Cmd.ofPromise postCsv file FetchSuccess FetchFailure

And the function to call the api with fetch:

let postData input =
    let formData = FormData.Create()
    formData.append("file.csv", input?files)
    let defaultProps =
        [ RequestProperties.Method HttpMethod.POST
        ; RequestProperties.Body (formData |> unbox)]
    promise {
        return! Fable.PowerPack.Fetch.fetch ("/api/data") defaultProps
    }

How can I can convert the React.FormEvent into the BodyInit that fetch needs?

glennsl
  • 28,186
  • 12
  • 57
  • 75
Dan O'Leary
  • 2,660
  • 6
  • 24
  • 50

4 Answers4

4

Your going to need to create a new FormData object and append the file to it.

let formData = FormData.createNew formData.append("file", file.[0])

Then change your call to postRecord to pass formData instead of file. You need fetch to encode it, but you are just passing the raw array of files.

At least that is my understanding from the fetch example on uploading a file. Also, post record looks wrong to me, is there just a "post" method you could use? postRecord implies it is expecting a record type.

Mizzle-Mo
  • 598
  • 2
  • 5
  • 11
  • I've updated the question to include my latest code, taking into account your suggestions. This actually makes the post request, but the file is not included. The request payload looks like this: ------WebKitFormBoundaryAkHYH5XIH8vT8kea Content-Disposition: form-data; name="file.csv" [object FileList] ------WebKitFormBoundaryAkHYH5XIH8vT8kea-- – Dan O'Leary Apr 25 '17 at 20:36
  • Please search your bundle in the public folder and post the code that is being generated. Also if you can please post a small repro project including the server; we can use it to troubleshoot or patch the fetch declaration in Powerpack. – Mizzle-Mo Apr 27 '17 at 13:21
1

I think Fetch.postRecord is your problem, it sets Content-Type header to application/json, when it should be multipart/form-data.

You need to use raw fetch API, instead of powerpack wrapper, something like this.

Eugene Tolmachev
  • 822
  • 4
  • 14
1

Upon testing your code, checking ev.target?value revealed that the event just grabs the name of the selected file. postRecord appears to be used for sending json to an endpoint. You are going to want to port: https://medium.com/ecmastack/uploading-files-with-react-js-and-node-js-e7e6b707f4ef , to what you want to do.

czifro
  • 784
  • 7
  • 24
1

It turns out I wasn't using the FileList API properly. Here's the working solution for the post method:

let postData input =
let formData = FormData.Create()
formData.append("file.csv", input?files?item(0))
let defaultProps =
    [ RequestProperties.Method HttpMethod.POST
    ;RequestProperties.Headers [unbox EncType "multipart/form-data"]
    ; RequestProperties.Body (formData |> unbox)]
promise {
    return! Fable.PowerPack.Fetch.fetch ("/api/data") defaultProps
}
Dan O'Leary
  • 2,660
  • 6
  • 24
  • 50