Javascript FormData不';部署Redux和AXIOS时,不要在React项目中上载文件
我有一个使用Redux和AXIOS的完整MERN堆栈项目。我使用FormData将图像上传到我的节点服务器,该服务器有multer,它在我的本地主机上运行得非常好,即使我的chrome上的控制台说空?(FormData{})。部署时,我的FormData为空。因此,我在没有文件的情况下测试了我的FormData(只是表单的输入值),它将传递到服务器,并将其保存在req.body上 我试图添加配置我的formData,但没有成功 我做错了什么 例如Javascript FormData不';部署Redux和AXIOS时,不要在React项目中上载文件,javascript,node.js,file,multipartform-data,form-data,Javascript,Node.js,File,Multipartform Data,Form Data,我有一个使用Redux和AXIOS的完整MERN堆栈项目。我使用FormData将图像上传到我的节点服务器,该服务器有multer,它在我的本地主机上运行得非常好,即使我的chrome上的控制台说空?(FormData{})。部署时,我的FormData为空。因此,我在没有文件的情况下测试了我的FormData(只是表单的输入值),它将传递到服务器,并将其保存在req.body上 我试图添加配置我的formData,但没有成功 我做错了什么 例如 config:{headers:{'Conten
config:{headers:{'Content Type':'multipart/form data'}
etc
以下是我的一些代码:
反应形式JS
import React, { Component, Fragment } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import TextAreaFieldGroup from "../common/TextAreaFieldGroup";
import InputGroup from "../common/InputGroup";
import { addEventful, upload } from "../../actions/eventfulActions";
import Dropzone from "react-dropzone";
const imageMaxSize = 10000000
; //bytes
const acceptedFileTypes =
"image/x-png, image/png, image/jpg, image/jpeg, image/gif";
const acceptedFileTypesArray = acceptedFileTypes.split(",").map(item => {
return item.trim();
});
class EventfulForm extends Component {
constructor(props) {
super(props);
this.state = {
eventtitle: "",
description: "",
// comments:'',
files: [],
errors: {}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
componentWillReceiveProps(newProps) {
if (newProps.errors) {
this.setState({ errors: newProps.errors });
}
}
verifyFile(files){
if(files && files.length > 0){
const currentFile = files[0]
const currentFileType = currentFile.type
const currentFileSize = currentFile.size
if(currentFileSize > imageMaxSize){
alert("TOO MANY FILES")
return false
}
if (!acceptedFileTypesArray.includes(currentFileType)) {
alert("IMAGES ONLY")
return false
}
return true
}
}
onSubmit(e) {
e.preventDefault();
const { user } = this.props.auth;
const formdata = new FormData();
this.state.files.forEach((file, i) => {
const newFile = { uri: file, type: "image/jpg" };
formdata.append("file", file, file.name);
});
// const newEventful = {
// eventtitle: this.state.eventtitle,
// description: this.state.description,
// pictures: this.state.pictures,
// name: user.name
// };
formdata.append("eventtitle", this.state.eventtitle);
formdata.append("description", this.state.description);
formdata.append("name", user.name);
this.props.addEventful(formdata);
this.setState({ eventtitle: "" });
this.setState({ description: "" });
this.setState({ files: [] });
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onDrop = (files, rejectedFiles) => {
if(rejectedFiles && rejectedFiles.length > 0){
console.log(rejectedFiles)
this.verifyFile(rejectedFiles)
}
if (files && files.length > 0) {
const isVerified = this.verifyFile(files)
if(isVerified){
console.log(files[0].name);
const formdata = new FormData();
files.map(file => {
formdata.append("file", file, file.name);
});
// formdata.append("file", files[0], files[0].name);
console.log(formdata);
// this.props.upload(formdata);
this.setState({
files: files
});
}
}
};
render() {
const previewStyle = {
display: "inline",
width: 100,
height: 100
};
const { errors, files } = this.state;
return (
<div className="post-form mb-3">
<div className="card card-info">
<div className="card-header bg-info text-white">Create an Event</div>
<div className="card-body">
<form onSubmit={this.onSubmit}>
<div className="form-group">
<InputGroup
placeholder="Create a event title"
name="eventtitle"
value={this.state.eventtitle}
onChange={this.onChange}
error={errors.eventtitle}
/>
{files.length > 0 && (
<Fragment>
<h3>Files name</h3>
{files.map((picture, i) => (
<p key={i}>{picture.name}</p>
))}
</Fragment>
)}
<Dropzone
onDrop={this.onDrop.bind(this)}
accept={acceptedFileTypes}
maxSize={imageMaxSize}
>
<div>
drop images here, or click to select images to upload.
</div>
</Dropzone>
<TextAreaFieldGroup
placeholder="Description"
name="description"
value={this.state.description}
onChange={this.onChange}
error={errors.description}
/>
</div>
<button type="submit" className="btn btn-dark">
Submit
</button>
</form>
</div>
</div>
</div>
);
}
}
EventfulForm.propTypes = {
addEventful: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors,
eventful: state.files
});
export default connect(
mapStateToProps,
{ addEventful, upload }
)(EventfulForm);
node.js
import axios from "axios";
import {
ADD_EVENTFUL,
GET_ERRORS,
ADD_LIKE,
REMOVE_LIKE,
GET_EVENTFUL,
GET_EVENTFULS,
DELETE_EVENTFUL,
CLEAR_ERRORS,
EVENTFUL_LOADING,
UPLOAD_FILES
} from "./types";
const config = {
onUploadProgress: progressEvent =>
console.log(
"Upload Progress" +
Math.round((progressEvent.loaded / progressEvent.total) * 100) +
"%"
)
};
// Add eventful
export const addEventful = eventfulData => dispatch => {
dispatch(clearErrors());
// .post("/api/eventfuls", eventfulData, config)
axios({
method: 'post',
url: '/api/eventfuls',
data: eventfulData,
config: { headers: { 'Content-Type': 'multipart/form-data' } }
}).then(res =>
dispatch({
type: ADD_EVENTFUL,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const passport = require("passport");
const bodyParser = require("body-parser");
// Eventful model
const Eventful = require("../../models/Eventful");
const User = require("../../models/User");
// Validation
const validateEventfulInput = require("../../validation/eventful");
const validateCommentInput = require("../../validation/comment");
var multer = require("multer");
var fs = require("fs");
var path = require("path");
var btoa = require("btoa");
router.use(
bodyParser.urlencoded({
extended: false
})
);
router.use(bodyParser.json());
var storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, __dirname + "../../../uploads"); //you tell where to upload the files,
},
filename: function(req, file, cb) {
cb(null, file.fieldname + "-" + Date.now());
}
});
var upload = multer({
storage: storage
}).array("file");
router.use((request, response, next) => {
response.header("Access-Control-Allow-Origin", "*");
response.header(
"Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS"
);
response.header("Access-Control-Allow-Headers", "Content-Type");
next();
});
// @route POST api/eventfuls
// @desc Create eventful
// @access Private
router.post(
"/",
passport.authenticate("jwt", { session: false }),
(req, res) => {
upload(req, res, err => {
console.log("req.body!!!!!", req.body);
const { errors, isValid } = validateEventfulInput(req.body);
// Check Validation
if (!isValid) {
console.log(errors);
// If any errors, send 400 with errors object
return res.status(400).json(errors);
}
console.log("req.files!!!!!", req.files);
if (err) {
console.log(err);
res.status(404).json({
uploadFailed: "Upload failed"
});
} else {
let newArr = [];
for (let file of req.files) {
let fileReadSync = fs.readFileSync(file.path);
let item = {};
item.image = {};
item.image.data = fileReadSync;
item.image.contentType = "img/png";
newArr.push(item);
fs.unlink(file.path, function(err) {
if (err) {
console.log("error deleting image", file.path);
} else {
console.log("deleted image", file.path);
}
});
}
for (var i = 0; i < newArr.length; i++) {
var base64 = btoa(
new Uint8Array(newArr[i].image.data).reduce(
(data, byte) => data + String.fromCharCode(byte),
""
)
);
newArr[i].image.data = base64;
}
console.log("33333333333333333333", newArr);
const newEventful = new Eventful({
title: req.body.eventtitle,
description: req.body.description,
pictures: newArr,
user: req.user.id,
name: req.user.name
});
newEventful.save().then(eventful => res.json(eventful));
}
console.log("skipped....................");
}
);
}
);
多部分/表单数据内容类型必须指定边界
参数,这是您事先无法知道的
不要重写将由XHR/fetch自动设置的内容类型。我发现您的代码中有两个问题 第一个来自npm页面,共页 这不处理多部分实体,因为它们复杂且 典型的大型自然环境。对于多部分实体,您可能感兴趣 以下模块:
body解析器
不会填充req.body
,但由于您已经在使用multer
,下面是一个如何使用多部分/表单数据
填充req.body
的示例
app.post('/', upload.none(), function (req, res, next) {
// req.body contains the text fields
})
但是,由于您需要文件,而上述内容无法使用,因此可以使用upload.any()
Second您的中间件注入顺序错误
改变这个
var upload = multer({
storage: storage
}).array("file");
到
而不是
router.post(
"/",
passport.authenticate("jwt", { session: false }),
(req, res) => {
upload(req, res, err => {
//code
}
);
}
);
做
编辑1
添加到app.js或index.js或应用程序的起点
global.rootPath = __dirname;
global.rootPath
现在将拥有应用程序的完整路径。ex/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());
}
});
我已经在我自己的React代码中成功地使用了FormData()来上传文件,但由于某些我无法解释的原因,文件需要最后追加。我想知道这是否与之前提到边界参数要求的回复有关,并且在实际上传之前无法知道它 尝试先添加数据,然后再添加文件。我还会尝试将单个文件作为测试用例。再一次,最后一次 首先附加以下内容:
formdata.append("eventtitle", this.state.eventtitle);
formdata.append("description", this.state.description);
formdata.append("name", user.name);
那么就称之为:
this.state.files.forEach((file, i) => {
const newFile = { uri: file, type: "image/jpg" };
formdata.append("file", file, file.name);
});
希望能有帮助。作为记录,我也使用multer,但在服务器端使用multer时遇到了同样的问题。在文件之前添加数据是我需要的修复方法
问候,
DB使用multer时,图像存储在请求的file属性中,所以是req.file,但您有req.files。我不确定这是否是您唯一的问题,因为我看到其他人对stackOverflow发表了评论,但我认为这也是一个问题。做一个console.log(req.file)并确保我是正确的,但我的代码中也使用了multer,我的工作正常。我试过使用它,也试过不使用它。是一样的。你是说我需要设定边界?非常感谢。这是一种工作,现在我有新的错误在我的pm2日志,当我试图上传图片<代码>0 | server | 2019-01-14 02:56-07:00:错误:eNote:没有这样的文件或目录,打开“/var/www/LCTW/uploads/file-1547459776817”它是如何查找该文件的?它不是应该查找base64吗?在POST 500错误中,它是否与我的
var storage=multer.diskStorage({目的地:函数(req,file,cb){cb(null,uu dirname+“../../../../uploads”);//告诉在哪里上载文件,},文件名:函数(req,file,cb){cb cb(null,file.fieldname+“-”+Date.now();});
?@I.Y您可以fs.readFileSync(file.path)
因此,假设文件不在那里,那么您应该得到enoint
错误。我建议您始终使用创建路径!!。另外,我建议您在index.js中执行类似于global.rootPath=\uu dirname
的操作,并避免使用。/../..//code>。理想情况下,它应该看起来像path,join(\uu dirname,global.rootPath,“uploads”)
像myserver.js中这样<代码>app.use(path.join(uu dirname,global.rootPath,“uploads”)代码>我试过了,我的网关坏了
global.rootPath = __dirname;
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());
}
});
formdata.append("eventtitle", this.state.eventtitle);
formdata.append("description", this.state.description);
formdata.append("name", user.name);
this.state.files.forEach((file, i) => {
const newFile = { uri: file, type: "image/jpg" };
formdata.append("file", file, file.name);
});