Image Uploader in React by merging an object and DataForm file

108 Views Asked by At



I’ve been struggling to find a solution to upload an image and store it in the database.
Tried them all: Dropzone, react-dropzone, react-uploader, etc.....

The BackEnd API is expecting this:

{
  "course": {
    "title": "Hic et velit sed.",
    "subtitle": "Omnis quibusdam illum itaque.",
    "description": "Et ullam ipsum. Illum dolor odit. Id veritatis ducimus.",
    "end_date": "2020-02-01",
    "attachment_attributes": {
      "file": {
        --- here the uploaded data file ---
      }
    }
  }
}

My component is now only written with 1 input type=file to speed up this testing. The API requirements such as title, subtitle etc., will be hardcoded.

Here the component:

import React, { Component } from 'react';
import api from '../../../../helpers/API';

class Uploader extends Component {
  constructor(props) {
    super(props);
      this.state = {
        selectedFile: null
      }
  }
  onChangeHandler = event => {
    this.setState({
      selectedFile: event.target.files[0],
      loaded: 0,
    })
  }

  onClickHandler = () => {

   ** HARDCODED API REQUIREMENT**    
   const body = "course": {
      "title": "Hic et velit sed.",
      "subtitle": "Omnis quibusdam illum itaque.",
      "description": "Et ullam ipsum. Illum dolor odit. Id veritatis ducimus.",
      "end_date": "2020-02-01",
      "attachment_attributes": {
        "file": {
          --- here the uploaded file data ---
        }
      }
    }    

   ** THIS SHOULD HANDLE THE UPLOADED FILE DATA FORMAT **    
    const data = new FormData()
    data.append('file', this.state.selectedFile)

    return api
      .post("http://localhost:3000/api/v1/files", ??????). <— DATA or BODY?
      .then(res => { 
        console.log(res.statusText)
      })
      .catch(res => console.log('error ==> ', res))
  }

  render() {
    return (
      <>
        <div>
          <label>Upload Your File </label>
          <input 
            type="file"
            multiple onChange={this.onChangeHandler}/>
        </div>   
        <button type="button" className="btn btn-success btn-block" onClick={this.onClickHandler}>Upload</button>
      </>
    )
  }
}

export default Uploader

Issue

If I pass DATA, in the API call, the uploaded file get nicely stored in the Browser > Network > Header > Data Form section, as file: binary

And that is good.

But I need to pass the uploaded file as part of my BODY object. So, below is what I did, but it gets returned empty:

onClickHandler = () => {
    const data = new FormData()
    data.append('file', this.state.selectedFile)

    const body = {
      "course": {
        "title": "Hic et velit sed.",
        "subtitle": "Omnis quibusdam illum itaque.",
        "description": "Et ullam ipsum. Illum dolor odit. Id veritatis ducimus.",
        "end_date": "2020-02-01",
        "attachment_attributes": {
          "file": data  <-- trying to pass the uploaded DataForm here
        }
      }
    }

    return api
      .post("http://localhost:3000/api/v1/files", body)
      .then(res => { 
        console.log(res.statusText)
      })
      .catch(res => console.log('error ==> ', res))
  }

Unfortunately I get 422 error, {"errors":{"attachment.file":["blank"]}}

Any help is much appreciated. Thanks
Jow

1

There are 1 best solutions below

0
Joe On

OK, found the way.

It's impossible to call the DataForm of the uploaded file from the Object BODY.

What it needs to be done is to data.append each single item in the object.

Here the file:

const data = new FormData()
data.append('course[title]', "Hic et dddvelit sed.")
data.append('course[subtitle]', "Omnis quibusdam illum itaque.")
data.append('course[description]', "Et ullam ipsum. Illum dolor odit. Id veritatis ducimus.")
data.append('course[end_date]', "2020-02-01")
data.append('course[attachment_attributes][file]', this.state.selectedFile)

This way, the Browser > Network > Header > Data Form, will show each item passed correctly and the uploaded image will maintain the binary format.

Here a preview:

course[title]: 6666Hic et dddvelit sed.
course[subtitle]: Omnis quibusdam illum itaque.
course[description]: Et ullam ipsum. Illum dolor odit. Id veritatis ducimus.
course[end_date]: 2020-02-01
course[attachment_attributes][file]: (binary)

POST API will be looking like this:

return api
  .post("http://localhost:3000/api/v1/courses", data)
  .then(res => { 
    console.log(res.statusText)
  })
  .catch(res => console.log('error ==> ', res))

I really hope this can help someone else because it has driven me crazy for weeks.

Happy coding. Joe