如何将文件从 ReactJS 上传到 Express 端点

How to upload files from ReactJS to Express endpoint

本文关键字:Express 端点 ReactJS 文件      更新时间:2023-09-26

在我目前正在处理的应用程序中,有几个文件格式通过superagent提交到Express API端点。例如,图像数据是这样发布的:

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));
     }
}

this.state.image从文件输入中设置如下:

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

AWSAPI.uploadImage看起来像这样:

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;
  }

最后,文件接收端点如下所示:

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!');
  });
};

目前,接收终结点的 on('file') 函数永远不会被调用,因此不会发生任何操作。以前,我用 multer 而不是 Busboy 尝试过类似的方法,但没有更多的成功(req.body包含解码的图像文件,req.files为空)。

我在这里错过了什么吗?将文件从 (ReactJS) Javascript 应用程序上传到 Express API 端点的最佳方法是什么?

我认为超级代理设置了错误的内容类型application/x-form-www-encoded而不是multipart/form-data您可以使用附加方法解决此问题,如下所示:

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

有关附加方法的详细信息,请阅读此处的文档:http://visionmedia.github.io/superagent/#multipart-requests

由于这涉及 Nodejs 服务器脚本,我决定制作一个 GitHub 存储库而不是小提琴:https://github.com/furqanZafar/reactjs-image-upload

根据经验,使用 ajax 上传文件在使用 FormData 时有效,但是该文件必须是唯一的表单字段/数据。如果您尝试将其与其他数据(如用户名,密码或几乎任何数据)组合在一起,则无法正常工作。(可能有解决方法可以解决这个问题,但我不知道)

如果您需要发送用户名/密码,您应该将它们作为标头发送(如果可以的话)。

我采取的另一种方法是首先使用普通数据进行用户注册,然后在成功后,我将带有 FormData 的文件单独上传作为更新。

反应文件上传 iamges 组件:

  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"));

服务器端映像保存并复制:

除了快递,你需要npm安装"多方"。此示例使用多方来解析表单数据并提取图像文件信息。然后"fs"将临时上传的图像复制到更永久的位置。

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);
        });
      }); 
    }); 
  })
}