部署时,FormData 不适用于使用 Redux 和 AXIOS 的 React 项目中的上传文件

I.Y*_*I.Y 5 javascript multipartform-data file form-data node.js

我有一个包含 Redux 和 AXIOS 的完整 MERN 堆栈项目。我使用 FormData 将图像上传到我的带有 multer 的节点服务器,它在我的本地主机上工作得很好,即使我的 chrome 控制台说空?(FormData\xc2\xa0{})。部署后,我的 FormData 为空。因此,我在没有文件的情况下测试了 FormData(只是表单的输入值),并将其传递到服务器并将其放在 req.body 上。

\n\n

我尝试添加配置我的 formData 但没有成功。

\n\n

我究竟做错了什么???

\n\n

例如

\n\n

config: { headers: { \'Content-Type\': \'multipart/form-data\' } }ETC.....

\n\n

这是我的一些代码:

\n\n

反应 JS 表单

\n\n
import React, { Component, Fragment } from "react";\nimport PropTypes from "prop-types";\nimport { connect } from "react-redux";\nimport TextAreaFieldGroup from "../common/TextAreaFieldGroup";\nimport InputGroup from "../common/InputGroup";\nimport { addEventful, upload } from "../../actions/eventfulActions";\n\nimport Dropzone from "react-dropzone";\n\nconst imageMaxSize = 10000000\n; //bytes\nconst acceptedFileTypes =\n  "image/x-png, image/png, image/jpg, image/jpeg, image/gif";\nconst acceptedFileTypesArray = acceptedFileTypes.split(",").map(item => {\n  return item.trim();\n});\n\nclass EventfulForm extends Component {\n  constructor(props) {\n    super(props);\n    this.state = {\n      eventtitle: "",\n      description: "",\n      // comments:\'\',\n      files: [],\n      errors: {}\n    };\n\n    this.onChange = this.onChange.bind(this);\n    this.onSubmit = this.onSubmit.bind(this);\n  }\n\n\n  componentWillReceiveProps(newProps) {\n    if (newProps.errors) {\n      this.setState({ errors: newProps.errors });\n    }\n  }\n\n  verifyFile(files){\n    if(files && files.length > 0){\n      const currentFile = files[0]\n      const currentFileType = currentFile.type\n      const currentFileSize = currentFile.size\n      if(currentFileSize > imageMaxSize){\n        alert("TOO MANY FILES")\n        return false\n      }\n      if (!acceptedFileTypesArray.includes(currentFileType)) {\n        alert("IMAGES ONLY")\n        return false\n      }\n      return true\n\n    }\n  }\n  onSubmit(e) {\n    e.preventDefault();\n    const { user } = this.props.auth;\n\n\n\n    const formdata = new FormData();\n    this.state.files.forEach((file, i) => {\n      const newFile = { uri: file, type: "image/jpg" };\n      formdata.append("file", file, file.name);\n    });\n\n    // const newEventful = {\n    //   eventtitle: this.state.eventtitle,\n    //   description: this.state.description,\n    //   pictures: this.state.pictures,\n    //   name: user.name\n    // };\n\n    formdata.append("eventtitle", this.state.eventtitle);\n    formdata.append("description", this.state.description);\n    formdata.append("name", user.name);\n\n    this.props.addEventful(formdata);\n    this.setState({ eventtitle: "" });\n    this.setState({ description: "" });\n    this.setState({ files: [] });\n  }\n  onChange(e) {\n    this.setState({ [e.target.name]: e.target.value });\n  }\n\n  onDrop = (files, rejectedFiles) => {\n    if(rejectedFiles && rejectedFiles.length > 0){\n      console.log(rejectedFiles)\n      this.verifyFile(rejectedFiles)\n    }\n    if (files && files.length > 0) {\n      const isVerified = this.verifyFile(files)\n      if(isVerified){\n        console.log(files[0].name);\n        const formdata = new FormData();\n        files.map(file => {\n          formdata.append("file", file, file.name);\n        });\n        // formdata.append("file", files[0], files[0].name);\n\n        console.log(formdata);\n        // this.props.upload(formdata);\n        this.setState({\n          files: files\n        });\n      }\n    }\n  };\n\n  render() {\n    const previewStyle = {\n      display: "inline",\n      width: 100,\n      height: 100\n    };\n    const { errors, files } = this.state;\n\n    return (\n      <div className="post-form mb-3">\n        <div className="card card-info">\n          <div className="card-header bg-info text-white">Create an Event</div>\n          <div className="card-body">\n            <form onSubmit={this.onSubmit}>\n              <div className="form-group">\n                <InputGroup\n                  placeholder="Create a event title"\n                  name="eventtitle"\n                  value={this.state.eventtitle}\n                  onChange={this.onChange}\n                  error={errors.eventtitle}\n                />\n                {files.length > 0 && (\n                  <Fragment>\n                    <h3>Files name</h3>\n                    {files.map((picture, i) => (\n                      <p key={i}>{picture.name}</p>\n                    ))}\n                  </Fragment>\n                )}\n                <Dropzone\n                  onDrop={this.onDrop.bind(this)}\n                  accept={acceptedFileTypes}\n                  maxSize={imageMaxSize}\n                >\n                  <div>\n                    drop images here, or click to select images to upload.\n                  </div>\n                </Dropzone>\n\n\n                <TextAreaFieldGroup\n                  placeholder="Description"\n                  name="description"\n                  value={this.state.description}\n                  onChange={this.onChange}\n                  error={errors.description}\n                />\n              </div>\n              <button type="submit" className="btn btn-dark">\n                Submit\n              </button>\n            </form>\n          </div>\n        </div>\n      </div>\n    );\n  }\n}\n\nEventfulForm.propTypes = {\n  addEventful: PropTypes.func.isRequired,\n  auth: PropTypes.object.isRequired,\n  errors: PropTypes.object.isRequired\n};\nconst mapStateToProps = state => ({\n  auth: state.auth,\n  errors: state.errors,\n  eventful: state.files\n});\n\nexport default connect(\n  mapStateToProps,\n  { addEventful, upload }\n)(EventfulForm);\n
Run Code Online (Sandbox Code Playgroud)\n\n

我的 FormAction.js

\n\n
import axios from "axios";\n\nimport {\n  ADD_EVENTFUL,\n  GET_ERRORS,\n  ADD_LIKE,\n  REMOVE_LIKE,\n  GET_EVENTFUL,\n  GET_EVENTFULS,\n  DELETE_EVENTFUL,\n  CLEAR_ERRORS,\n  EVENTFUL_LOADING,\n  UPLOAD_FILES\n} from "./types";\n\nconst config = {\n  onUploadProgress: progressEvent =>\n    console.log(\n      "Upload Progress" +\n        Math.round((progressEvent.loaded / progressEvent.total) * 100) +\n        "%"\n    )\n};\n// Add eventful\nexport const addEventful = eventfulData => dispatch => {\n  dispatch(clearErrors());\n  // .post("/api/eventfuls", eventfulData, config)\n\n  axios({\n    method: \'post\',\n    url: \'/api/eventfuls\',\n    data: eventfulData,\n    config: { headers: { \'Content-Type\': \'multipart/form-data\' } }\n\n  }).then(res =>\n      dispatch({\n        type: ADD_EVENTFUL,\n        payload: res.data\n      })\n    )\n    .catch(err =>\n      dispatch({\n        type: GET_ERRORS,\n        payload: err.response.data\n      })\n    );\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

节点.js

\n\n
const express = require("express");\nconst router = express.Router();\nconst mongoose = require("mongoose");\nconst passport = require("passport");\nconst bodyParser = require("body-parser");\n\n// Eventful model\nconst Eventful = require("../../models/Eventful");\nconst User = require("../../models/User");\n\n// Validation\nconst validateEventfulInput = require("../../validation/eventful");\nconst validateCommentInput = require("../../validation/comment");\n\nvar multer = require("multer");\n\nvar fs = require("fs");\nvar path = require("path");\n\nvar btoa = require("btoa");\n\nrouter.use(\n  bodyParser.urlencoded({\n    extended: false\n  })\n);\nrouter.use(bodyParser.json());\n\nvar storage = multer.diskStorage({\n  destination: function(req, file, cb) {\n    cb(null, __dirname + "../../../uploads"); //you tell where to upload the files,\n  },\n  filename: function(req, file, cb) {\n    cb(null, file.fieldname + "-" + Date.now());\n  }\n});\n\nvar upload = multer({\n  storage: storage\n}).array("file");\n\nrouter.use((request, response, next) => {\n  response.header("Access-Control-Allow-Origin", "*");\n  response.header(\n    "Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS"\n  );\n  response.header("Access-Control-Allow-Headers", "Content-Type");\n  next();\n});\n\n// @route   POST api/eventfuls\n// @desc    Create eventful\n// @access  Private\nrouter.post(\n  "/",\n  passport.authenticate("jwt", { session: false }),\n  (req, res) => {\n    upload(req, res, err => {\n      console.log("req.body!!!!!", req.body);\n      const { errors, isValid } = validateEventfulInput(req.body);\n\n      // Check Validation\n      if (!isValid) {\n        console.log(errors);\n        // If any errors, send 400 with errors object\n        return res.status(400).json(errors);\n      }\n\n      console.log("req.files!!!!!", req.files);\n      if (err) {\n        console.log(err);\n        res.status(404).json({\n          uploadFailed: "Upload failed"\n        });\n      } else {\n        let newArr = [];\n\n        for (let file of req.files) {\n          let fileReadSync = fs.readFileSync(file.path);\n          let item = {};\n          item.image = {};\n          item.image.data = fileReadSync;\n          item.image.contentType = "img/png";\n          newArr.push(item);\n\n          fs.unlink(file.path, function(err) {\n            if (err) {\n              console.log("error deleting image", file.path);\n            } else {\n              console.log("deleted image", file.path);\n            }\n          });\n        }\n        for (var i = 0; i < newArr.length; i++) {\n          var base64 = btoa(\n            new Uint8Array(newArr[i].image.data).reduce(\n              (data, byte) => data + String.fromCharCode(byte),\n              ""\n            )\n          );\n          newArr[i].image.data = base64;\n        }\n\n        console.log("33333333333333333333", newArr);\n\n        const newEventful = new Eventful({\n          title: req.body.eventtitle,\n          description: req.body.description,\n          pictures: newArr,\n          user: req.user.id,\n          name: req.user.name\n        });\n\n        newEventful.save().then(eventful => res.json(eventful));\n      }\n      console.log("skipped....................");\n    }\n  );\n  }\n);\n
Run Code Online (Sandbox Code Playgroud)\n\n

我的 PM2 上的错误/日志

\n\n
\n

0|服务器| 2019-01-13 21:27 -07:00:服务器已准备好接收消息\n 0|服务器 | 2019-01-13 21:28-07:00:请求正文!!!!! [对象:null\n 原型] {} 0|服务器 | 2019-01-13 21:28-07:00:请求文件!!!!!\n [] 0|服务器 | 2019-01-13 21:28 -07:00: { [错误: ENOENT: 没有这样的\n文件或目录,打开 \'/var/www/LCTW/uploads/file-1547440111023\']\n 0|服务器| 2019-01-13 21:28 -07:00: 错误号: -2, 0|服务器 |\n 2019-01-13 21:28 -07:00: 代码: \'ENOENT\', 0|服务器 | 2019-01-13\n 21:28 -07:00: 系统调用: \'open\', 0|server | 2019-01-13 21:28 -07:00:\n 路径: \'/var/www/LCTW/uploads/file-1547440111023\', 0|服务器 |\n 2019-01-13 21:28 -07 :00: 存储错误: [] }

\n
\n\n

在这里我的 req.body 和 req.files 是空的。\n但是

\n\n

当我在 Node.js 上注释掉文件部分时,req.body 存在!

\n\n
0|server   | 2019-01-13 21:40 -07:00: req.body!!!!! [Object: null prototype] {\n0|server   | 2019-01-13 21:40 -07:00:   eventtitle: \'asdfas\',\n0|server   | 2019-01-13 21:40 -07:00:   description: \'asdfads\',\n0|server   | 2019-01-13 21:40 -07:00:   name: \'In Soo Yang\' }\n
Run Code Online (Sandbox Code Playgroud)\n

Sta*_*mos 3

我可以在你的代码中看到两个问题

首先从body-parser的 npm 页面

由于其复杂且通常较大的性质,这不能处理多部分主体。对于多部分主体,您可能对以下模块感兴趣:

  1. 巴士男孩和连接巴士男孩
  2. 多方和连接多方
  3. 强大
  4. 穆尔特

所以body-parser不会填充 ,req.body但由于您已经在使用multer这里是一个关于如何使用 . 填充 的req.body示例multipart/form-data

app.post('/', upload.none(), function (req, res, next) {
  // req.body contains the text fields
})
Run Code Online (Sandbox Code Playgroud)

但由于您需要文件而上述内容不起作用,您可以使用upload.any()

其次,您的中间件注入顺序错误。

改变这个

var upload = multer({
  storage: storage
}).array("file");
Run Code Online (Sandbox Code Playgroud)

var upload = multer({
  storage: storage
})
Run Code Online (Sandbox Code Playgroud)

而不是

router.post(
  "/",
  passport.authenticate("jwt", { session: false }),
  (req, res) => {
    upload(req, res, err => {

     //code

    }
  );
  }
);
Run Code Online (Sandbox Code Playgroud)

router.post(
  "/",
  passport.authenticate("jwt", { session: false }),
  upload.array("file"), //or upload.any()
  (req, res) => {

    //....code
    //now req.body sould work
    //file should be at req.files

  );
  }
);
Run Code Online (Sandbox Code Playgroud)

编辑1

添加 app.js 或 index.js 或您应用程序的起点

global.rootPath = __dirname;
Run Code Online (Sandbox Code Playgroud)

global.rootPath现在将拥有您的应用程序的完整路径。前 /usr/user/Desktop/myapp 使用path,join(global.rootPath, "uploads")会给你/usr/user/Desktop/myapp/uploads。使用的好处path.join是它可以处理不同的操作系统路径系统,例如 Windows 和 *nix

始终用于path.join创建所有路径。

var storage = multer.diskStorage({
  destination: function(req, file, cb) {
    cb(null, path.join(global.rootPath, "uploads")); //you tell where to upload the files,
  },
  filename: function(req, file, cb) {
    cb(null, file.fieldname + "-" + Date.now());
  }
});
Run Code Online (Sandbox Code Playgroud)