admin管理员组

文章数量:1297120

In the application I'm currently working on, there are a couple of file forms that are submitted via superagent to an Express API endpoint. For example, image data is posted like so:

handleSubmit: function(evt) {
    var imageData = new FormData();

    if ( this.state.image ) {
       imageData.append('image', this.state.image);
       AwsAPI.uploadImage(imageData, 'user', user.id).then(function(uploadedImage) {
         console.log('image uploaded:', uploadedImage);
       }).catch(function(err) {
         this.setState({ error: err });
       }.bind(this));
     }
}

and this.state.image is set like this from a file input:

updateImage: function(evt) {
    this.setState({
      image: evt.target.files[0]
    }, function() {
      console.log('image:', this.state.image);
    });
  },

AWSAPI.uploadImage looks like this:

uploadImage: function(imageData, type, id) {
    var deferred = when.defer();

    request.put(APIUtils.API_ROOT + 'upload/' + type + '/' + id)
    .type('form')
    .send(imageData)
    .end(function(res) {
      if ( !res.ok ) {
        deferred.reject(res.text);
      } else {
        deferred.resolve(APIUtils.normalizeResponse(res));
      }
    });

    return deferred.promise;
  }

And lastly, the file receiving endpoint looks like this:

exports.upload = function(req, res) {

  req.pipe(req.busboy);

  req.busboy.on('file', function(fieldname, file) {
    console.log('file:', fieldname, file);
    res.status(200).send('Got a file!');
  });

};

Currently, the receiving endpoint's on('file') function never gets called and so nothing happens. Previously, I've tried similar approaches with multer instead of Busboy with no more success (req.body contained the decoded image file, req.files was empty).

Am I missing something here? What is the best approach to upload files from a (ReactJS) Javascript app to an Express API endpoint?

In the application I'm currently working on, there are a couple of file forms that are submitted via superagent to an Express API endpoint. For example, image data is posted like so:

handleSubmit: function(evt) {
    var imageData = new FormData();

    if ( this.state.image ) {
       imageData.append('image', this.state.image);
       AwsAPI.uploadImage(imageData, 'user', user.id).then(function(uploadedImage) {
         console.log('image uploaded:', uploadedImage);
       }).catch(function(err) {
         this.setState({ error: err });
       }.bind(this));
     }
}

and this.state.image is set like this from a file input:

updateImage: function(evt) {
    this.setState({
      image: evt.target.files[0]
    }, function() {
      console.log('image:', this.state.image);
    });
  },

AWSAPI.uploadImage looks like this:

uploadImage: function(imageData, type, id) {
    var deferred = when.defer();

    request.put(APIUtils.API_ROOT + 'upload/' + type + '/' + id)
    .type('form')
    .send(imageData)
    .end(function(res) {
      if ( !res.ok ) {
        deferred.reject(res.text);
      } else {
        deferred.resolve(APIUtils.normalizeResponse(res));
      }
    });

    return deferred.promise;
  }

And lastly, the file receiving endpoint looks like this:

exports.upload = function(req, res) {

  req.pipe(req.busboy);

  req.busboy.on('file', function(fieldname, file) {
    console.log('file:', fieldname, file);
    res.status(200).send('Got a file!');
  });

};

Currently, the receiving endpoint's on('file') function never gets called and so nothing happens. Previously, I've tried similar approaches with multer instead of Busboy with no more success (req.body contained the decoded image file, req.files was empty).

Am I missing something here? What is the best approach to upload files from a (ReactJS) Javascript app to an Express API endpoint?

Share Improve this question edited Nov 20, 2014 at 18:30 Jakemmarsh asked Nov 20, 2014 at 17:16 JakemmarshJakemmarsh 4,67112 gold badges44 silver badges71 bronze badges 3
  • did u try socket.io? it can be used to transmit binary data and jsons – Dmitry Matveev Commented Nov 20, 2014 at 17:51
  • @DmitryMatveev I think socket.io would be a little overkill just for sending some JSON/files from client to server unfortunately. I don't really need all the real-time features – Jakemmarsh Commented Nov 20, 2014 at 18:00
  • What frontend are you using. If not so specific, you can give it a try for jQuery Form plugin. It emits necessary events like beforeSubmit, uploadProgress, success, error. Server side, you can use Node Formidable. – Palak Bhansali Commented Dec 5, 2014 at 7:36
Add a ment  | 

3 Answers 3

Reset to default 7 +50

I think superAgent is setting the wrong content-type of application/x-form-www-encoded instead of multipart/form-data you can fix this by using the attach method like so:

request.put(APIUtils.API_ROOT + 'upload/' + type + '/' + id)
    .attach("image-file", this.state.image, this.state.image.name)
    .end(function(res){
        console.log(res);
    });

for more information about the attach method, read the documentation here: http://visionmedia.github.io/superagent/#multipart-requests

since this involves a nodejs server script I decided to make a GitHub repo instead of a fiddle: https://github./furqanZafar/reactjs-image-upload

From experience, uploading a file using ajax works when you use FormData, however the file must be the only form field / data. If you try and bine it with other data (like username, password or pretty much anything at all) it does not work. (Possibly there are work arounds to get around that issue, but I am not aware of any)

If you need to send the username/password you should be sending those as headers if you can instead.

Another approach I took was first do the user registration with the normal data, then on success I upload the file with the FormData separately as an update.

The react file upload iamges ponent:

  class ImageUpload extends React.Component {
    constructor(props) {
      super(props);
      this.state = {file: '',imagePreviewUrl: ''};
    }

  _handleSubmit(e) {
    e.preventDefault();
    // this.uploadImage()
    // TODO: do something with -> this.state.file
    console.log('handle uploading-', this.state.file);    }

   _handleImageChange(e) {
     e.preventDefault();

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

    reader.onloadend = () => {
      this.setState({
        file: file,
        imagePreviewUrl: reader.result
      });
    }

    reader.readAsDataURL(file)   }

       // XHR/Ajax file upload     uploadImage(imageFile) {
    return new Promise((resolve, reject) => {
      let imageFormData = new FormData();

      imageFormData.append('imageFile', imageFile);

      var xhr = new XMLHttpRequest();

      xhr.open('post', '/upload', true);

      xhr.onload = function () {
        if (this.status == 200) {
          resolve(this.response);
        } else {
          reject(this.statusText);
        }
      };

       xhr.send(imageFormData);

     });    }

  render() {
    let {imagePreviewUrl} = this.state;
    let $imagePreview = null;
    if (imagePreviewUrl) {
      $imagePreview = (<img src={imagePreviewUrl} />);
    } else {
      $imagePreview = (<div className="previewText">Please select an Image for Preview</div>);
    }

    return (
      <div className="previewComponent">
        <form onSubmit={(e)=>this._handleSubmit(e)}>
          <input className="fileInput" type="file" onChange={(e)=>this._handleImageChange(e)} />
          <button className="submitButton" type="submit" onClick={(e)=>this._handleSubmit(e)}>Upload Image</button>
        </form>
        <div className="imgPreview">
          {$imagePreview}
        </div>
      </div>
    )   } }    React.render(<ImageUpload/>, document.getElementById("mainApp"));

The Server Side Image Save and Copy:

Along with express You needed to npm install 'multiparty'. This example uses multiparty to parse the form data and extract the image file information. Then 'fs' to copy the temporarily upload image to a more permanent location.

let multiparty = require('multiparty');
let fs = require('fs');

function saveImage(req, res) {
  let form = new multiparty.Form();

  form.parse(req, (err, fields, files) => {

    let {path: tempPath, originalFilename} = files.imageFile[0];
    let newPath = "./images/" + originalFilename;

    fs.readFile(tempPath, (err, data) => {
      // make copy of image to new location
      fs.writeFile(newPath, data, (err) => {
        // delete temp image
        fs.unlink(tempPath, () => {
          res.send("File uploaded to: " + newPath);
        });
      }); 
    }); 
  })
}

本文标签: javascriptHow to upload files from ReactJS to Express endpointStack Overflow