0

I am trying to send data to a rest api using react hosted in a separate application and the api does not recieve the keys sent even though I logged the results in Chrome and got this:(2) ["imageSrc", File]0: "imageSrc"1: File {name: "22196156_10154999506505959_5971486080451335945_n.jpg", lastModified: 1507302065124, lastModifiedDate: Fri Oct 06 2017 16:01:05 GMT+0100 (WAT), webkitRelativePath: "", size: 32766, …}length: 2__proto__: Array(0)

This is my api post controller

module.exports.createImage = function(req, res, next){
  Image.
    create({
      imageSrc: req.body.image,
      post: req.body.post
    }, function(err, image){
      if(err){
        return next(err)
      }
      res.status(201).json(image);
    });
}

This is my react form page:

handleSubmit(e){
    e.preventDefault();
    if(!this.uploadFile){
      return;
    }
    let data = new FormData();
    data.append('imageSrc', this.uploadFile);
    data.append('post', this.state.value);
    console.log(...data);

    fetch('http://127.0.0.1:4400/api/images', {
      method: 'POST',
      body: data
    }).then((res) => {
      this.setState({
        status: 'uploading',
        statusMsg: (<p>Uploading...</p>)
      });
      console.log(res)
      return res.json();
    }).then((val) => {
      if(val.message == 'ok'){
        this.setState({
          status: 'done',
          statusMsg: (<p id='checkMark'><i className="fa fa-check"></i></p>)
        });
        console.log(val)

        timer = _.delay(this.setOriginalText, 1000);
      }
    }).catch(error =>{
      return error;
    });
  }

  handleTextChange(e){
    this.setState({value: e.target.value});
  }

  handleImageChange(e){
    e.preventDefault();
    let reader = new FileReader();
    let file = e.target.files[0];

    reader.onloadend = () => {
      this.setState({
        imagePreviewUrl: reader.result,
        style: {background: ''}
      });
      this.uploadFile = file;
    };
    
    reader.readAsDataURL(file);
  }
  
  render(){
    let{imagePreviewUrl} = this.state;
    let imagePreview = this.state.statusMsg;
    if(imagePreviewUrl){
      imagePreview = (<img src={imagePreviewUrl} className="dropPreview" />);
    }
    return(
      <div className="container">
      <form>
        <div
          onDragOver={this.onDragOver}
          onDragLeave={this.onDragLeave}
          className="dropZoneContainer">

          <div className="dropZone" id="upload-file-container" style={this.state.style}>{imagePreview}
            <input type="file" name="imageSrc" onChange={this.handleImageChange} />
          </div>

          <label htmlFor="post">Post:</label> 
            
            <textarea value={this.state.value} name="post" placeholder="Write something related to the picture" onChange={this.handleTextChange} />
                   
        </div>
        <a href="" onClick={this.handleSubmit} className="icon-button cloudicon">
            <i className="fa fa-cloud-upload"></i><span>Post</span>
        </a>
      </form>
      </div>
    );
  }
}

I don't know what I might be doing wrong, and I'd appreciate any help.

Edit: Class constructor.

class PostUpload extends React.Component{

  constructor(props){
    super(props);
    this.state = {
      imagePreviewUrl: '',
      status: 'idle',
      statusMsg: (<p>Click or drop files here to upload</p>),
      style: {},
      value: ''
    };

    this.uploadFile = '';
    this.handleTextChange = this.handleTextChange.bind(this);
    this.handleImageChange = this.handleImageChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.onDragOver = this.onDragOver.bind(this);
    this.onDragLeave = this.onDragLeave.bind(this);
    this.setOriginalText = this.setOriginalText.bind(this);
  }
Nnanyielugo
  • 401
  • 4
  • 13
  • Did you check if any request arrives at `/api/images` endpoint? Did you bind the methods you are firing to upload and submit the image? – Dez Jan 20 '18 at 15:04
  • I checked. The request does arrive, but the request body is either empty, or strangely does not contain the expected keys. The console produces this error message: POST /api/images 500 21.194 ms - 1039 ValidationError: image validation failed: imageSrc: Path `imageSrc` is required., post: Path `post` is required. – Nnanyielugo Jan 20 '18 at 15:17
  • As per your second question, I'm not sure I understand you. – Nnanyielugo Jan 20 '18 at 15:18
  • Then, add your class constructor to the code, please. – Dez Jan 20 '18 at 15:53
  • Done. Please check edit. – Nnanyielugo Jan 20 '18 at 16:18
  • Check this answer related to Express and receiving form with images, because you are missing some stuff (for example the form encoding and setting the proper Content-Type in the fetch headers): https://stackoverflow.com/questions/27045598/how-to-upload-files-from-reactjs-to-express-endpoint – Dez Jan 20 '18 at 16:40
  • I added Content-Type headers and response shows "400: Bad request" – Nnanyielugo Jan 21 '18 at 14:10

1 Answers1

0

You need to use a library to parse the form you are sending with fetch to Express. For example, I used multiparty (so you don't need to set any header with the Content-Type in the fetch request).

const fs = require('fs');
const express = require('express');
const bodyParser = require('body-parser');
const multiparty = require('multiparty');

const app = express();
const port = 4400;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

...

app.post('/api/images', function(req, res, next) {
  const form = new multiparty.Form();
  form.parse(req, (err, fields, files) => {
    if (err) {
      return next(err);
    }
    // console.log(fields); /* fields, where you will find the { post } info. */
    // console.log(files); /* files, where you will find the file uploaded. */
    try {
      const imgPath = files.imageSrc[0].path;
      const img = fs.readFileSync(imgPath);
      res.writeHead(200, { 'Content-Type': files.imageSrc[0].headers['content-type'] });
      res.end(img, 'binary');
    } catch(Error) {
      res.status(400).send('Error when creating image');
    }
  });
  return;
});

I uploaded a working project with the implemented solution I propose to you in this Github repository, so you can check the code if you want.

Dez
  • 5,702
  • 8
  • 42
  • 51