I am using ReactJS to create a simple sendMail app. And the problem is I do not receive req.file in my server (ExpressJS).
myForm.js
<form className="content-container mailing-form" encType='multipart/form-data'>
<h1>Let's see if it works</h1>
<div>
<label>Mail: </label>
<input type="text" value={this.state.email} onChange={this.handleChangeToMail} style={{ width: '50%' }} />
</div>
<div>
<label>Subject: </label>
<input type="text" value={this.state.subject} onChange={this.handleChangeSubject} style={{ width: '50%' }} />
</div>
<div>
<textarea
id="test"
name="test-mailing"
onChange={this.handleChangeFeedback}
placeholder="Post some lorem ipsum here"
required
value={this.state.feedback}
style={{ width: '100%', height: '150px' }}
/>
</div>
<div> //Here is my input file
<label>File: </label>
<input
type="file"
onChange={this.handleChangeFile}
name="myFile"
/>
<img src="" id="preview" alt="Image preview..."></img>
</div>
<input type="button" value="Submit" onClick={this.handleSubmit} />
</form>
handleChangeFile
handleChangeFile = e => {
e.preventDefault();
let fReader = new FileReader();
let file = '';
fReader.readAsDataURL(e.target.files[0]);
fReader.onloadend = (event) => {
document.getElementById('preview').src = event.target.result;
file = event.target.result;
this.setState(() => ({ file }));
console.log(this.state.file);
}
}
handleSubmit
handleSubmit = e => {
let formData = new FormData();
formData.append('name', this.state.name);
formData.append('email', this.state.email);
formData.append('feedback', this.state.feedback);
formData.append('subject', this.state.subject);
formData.append('file', this.state.file);
const config = {
headers: {
"Content-Type": "multipart/form-data",
"Accept": "application/json",
"type": "formData"
}
}
const url = "http://localhost:8081/send";
axios
.post(
url,
formData,
config
)
.then(res => console.log(res.data))
.catch(err => console.log('Error: ', err));
}
Server.js
const nodemailer = require('nodemailer');
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
var multer = require('multer');
const app = express();
const port = 8081;
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next(); });
app.get('/', (req, res) => res.send('......!'));
const storage = multer.diskStorage({
destination: './uploads',
filename: function (req, file, cb) {
cb(null, file.fieldname + Date.now());
} })
const uploads = multer({
storage: storage
}).single('myFile');
app.post('/send', uploads, (req, res) => {
if (req.file) {
res.send('Uploaded !');
} else {
res.send('Fail !!');
}
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
And I keep receive "Fail !!" message on my form.
I have tried to add any solution I can find. I know it can be some mess in there, sorry for that :') !
I solved my problem, thanks to this thread : Convert Data URI to File then append to FormData
I add a function to convert file from weird base64 string to Blob type before send it to server. Then my server can receive req.file !!
1.make your uploads folder as static folder
app.use(express.static('uploads'));
2.change your destination like below
destination : function( req , file , cb ){
cb(null,'./uploads');
},
append your file as myFile in frontend
formData.append('myFile', this.state.file);
Related
I am making a pinterest clone so I need to upload images but I cant fetch files from frontend to back end(with axios)
this is the server
I tested so many stuff but I can't understand where is the flaw. The app stores the photo and it seems that the problem happens when I want to get the data to backend but only files cuz I can fetch string from frontend
const express = require('express');
const app = express()
const mongoose = require('mongoose')
const cors = require('cors')
const Photo = require('./models/Photo')
const multer = require('multer')
//MiddleWare
app.use(cors())
app.use(express.json())
mongoose.connect('', () =>{
console.log('Connected to DB..')
})
// storage
const Storage = multer.diskStorage({
destination: 'uploads',
filename: (req, file,cb) =>{
cb(null, file.originalname)
}
})
const upload = multer({
storage: Storage,
}).single('testImage')
app.post('/add', async(req, res) =>{
console.log(req.files)
})
app.listen(3001, () =>{
console.log('Listening..')
})
And this is client side
/* eslint-disable jsx-a11y/alt-text */
/* eslint-disable react/jsx-no-comment-textnodes */
import React, { useState, useRef } from "react";
import NotificationsIcon from "#mui/icons-material/Notifications";
import AddAPhotoIcon from "#mui/icons-material/AddAPhoto";
import "./Header.css";
import axios from "axios"
const Header = ({gallery, setGallery}) => {
const [open, setOpen] = useState(false);
const [photo, setPhoto] = useState('')
const handleModel = (e) => {
setOpen(!open);
};
const handlePhoto = (e) => {
setPhoto(e.target.files[0])
}
const handleSubmit = e=>{
e.preventDefault();
setOpen(!open);
console.log(photo)
axios.post('http://localhost:3001/add',{photo: photo})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
}
return (
<div className="header">
<div className="nav">
<img
src="https://i.pinimg.com/originals/0d/ea/4a/0dea4ad3030467e2f65cde00935ba62b.png"
className="logo"
/>
<input type="text" className="search-input" placeholder="Search" />
<NotificationsIcon color="action" fontSize="large" className="icon" />
<AddAPhotoIcon
color="action"
fontSize="large"
className="add-icon icon"
onClick={handleModel}
/>
</div>
{open ? (
<form onSubmit={handleSubmit} className="popup" encType='multipart/form-data'>
<input
type="file"
accept=".png, .jpg, .jpeg"
filename='testImage'
onChange={handlePhoto}
/>
<button >Submit</button>
</form>
) : (
""
)}
</div>
);
};
export default Header;
Server console logs undefined when I upload an image but why tho??
Multer returns you a middleware when you invoke the multer() function.
so in your case you should add the middleware to your route.
const upload = multer({
storage: Storage,
}).single('testImage');
app.post('/add', upload ,async(req, res) =>{
console.log(req.file)
})
Now your route '/add' will look for the key testImage in the multipart/form-data.
You need to pass the upload as formData
const formData = new FormData();
formData.append('files', photo); //this matches req.files
const res = await axios.post('http://localhost:3001/add', formData, {
headers: formData.getHeaders() //formData will set content-type etc
});
and update route to use middleware
app.post('/add', upload, async(req, res) => {})
This is my express server
const express = require("express");
const app = express();
const mongoose = require("mongoose");
require("dotenv").config();
const cors = require("cors");
const userData = require("./db");
var PORT = process.env.PORT || 3001;
app.use(cors());
app.use(express.urlencoded({ extended: false }));
mongoose
.connect("mongodb://localhost", {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log("Connected to database!!");
})
.catch((e) => {
console.log("Error : " + err);
});
app.get("/", (req, res) => {
res.json({ server: "Running" });
});
app.get("/login", (req, res) => {
res.json({ user: "true" });
});
app.post("/signup", (req, res) => {
console.log(req);
res.redirect("/");
});
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});
and this is my React component
import React, { useState } from "react";
import "./Signup.css";
import axios from "axios";
import { useSelector } from "react-redux";
import { selectAuth } from "../../features/authSlice";
export default function Login() {
const isAuthenticated = useSelector(selectAuth);
const [user, setUser] = useState("");
const [pass, setPass] = useState("");
const handleSignup = async (e) => {
e.preventDefault();
var bodyFormData = new FormData();
bodyFormData.append("username", user);
bodyFormData.append("password", pass);
const res = await axios({
method: "post",
url: "http://localhost:3001/signup",
data: bodyFormData,
headers: { "Content-Type": "multipart/form-data" },
});
};
return (
<div className="signup__container">
<h2>Signup</h2>
<form className="signup__form" autoComplete="off">
<div className="signup__imgContainer">
<img
src={require("../../assets/avatar-placeholder.jpg").default}
alt="Avatar"
className="signup__avatar"
/>
</div>
<div className="signup__inputContainer">
<label for="uname">
<b>Username</b>
</label>
<input
type="text"
placeholder="Enter Username"
name="uname"
onChange={(e) => setUser(e.target.value)}
required
/>
<label for="psw">
<b>Password</b>
</label>
<input
type="password"
placeholder="Enter Password"
name="psw"
required
onChange={(e) => setPass(e.target.value)}
/>
<button type="submit" onClick={handleSignup}>
Signup
</button>
<label>
<input type="checkbox" name="remember" /> Remember me
</label>
</div>
</form>
<div>{isAuthenticated.toString()}</div>
</div>
);
}
I am trying to post data using axios but I am unable to access this data in my backend. Am i doing it right, if yes then can anyone tell me what's wrong. and if no then how can I correct it to use the data.
I tried various things like sending the data as params but nothing worked on server it was always undefined however the console.log(req) gives something but I didn't see any of my post data in it.
add app.use(express.json());
if it does not work. then add body-parser in your package.
const express = require("express");
const bodyParser = require("body-parser"); // install body-parser
const cors = require("cors");
const app = express();
app.use(bodyParser.json({ limit: "50mb" }));
app.use(
bodyParser.urlencoded({
limit: "50mb",
extended: true,
parameterLimit: 50000,
})
);
app.use(express.json()); /// add
In the React app, you're using Content-Type multipart/form-data. When you use this Content-Type, the server needs to parse the request differently. This Content-Type is used when you want to upload a file in the request. It isn't the case here, so you should use Content-Type application/json instead.
Your request should be:
const res = await axios({
method: "post",
url: "http://localhost:3001/signup",
data: { username : user, password : pass},
headers: { "Content-Type": "application/json },
});
And in the Express application, you need to add a middleware to parse incoming requests in application/json Content-Type.
app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.use(express.json()); // <---- add this
In the route, you can see the data inside req.body
app.post("/signup", (req, res) => {
console.log(req.body);
res.redirect("/");
});
I'm trying to create login/registration for an app using React/Node/Express/Postgres. Where I'm getting stuck is receiving data on the server side from my form in React.
I have a register component for the form in register.js
import React from 'react';
import useForm from '../form/useForm';
const Register = () => {
const { values, handleChange, handleSubmit } = useForm({
name: '',
email: '',
password: "",
password2: ""
}, register);
function register() {
console.log(values);
}
return (
<div className="row mt-5">
<div className="col-md-6 m-auto">
<div className="card card-body">
<h1 className="text-center mb-3">
<i className="fas fa-user-plus"></i> Register
</h1>
<form
action="/users/register"
method="POST"
onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="name">Name</label>
<input
className="form-control"
type="name"
name="name"
onChange={handleChange}
placeholder="Enter Name"
value={values.name}
required />
</div>
<div className="form-group">
<label htmlFor="email">Email</label>
<input
className="form-control"
type="email"
name="email"
onChange={handleChange}
placeholder="Enter Email"
value={values.email}
required />
</div>
<div className="form-group">
<label htmlFor="email">Password</label>
<input
className="form-control"
type="password"
name="password"
onChange={handleChange}
placeholder="Create Password"
value={values.password}
required />
</div>
<div className="form-group">
<label htmlFor="email">Confirm Password</label>
<input
className="form-control"
type="password"
name="password2"
onChange={handleChange}
placeholder="Confirm Password"
value={values.password2}
required />
</div>
<button type="submit" className="btn btn-primary btn-block">
Register
</button>
</form>
<p className="lead mt-4">Have An Account? Login</p>
</div>
</div>
</div>
);
};
export default Register;
A hook to handle the form actions in useForm.js
import {useState, useEffect} from 'react';
const useForm = (initialValues, callback) => {
const [hasError, setErrors] = useState(false);
const [values, setValues] = useState(initialValues);
const handleSubmit = (event) => {
if (event) event.preventDefault();
const options = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(setValues(values => ({ ...values, [event.target.name]: event.target.value })))
}
fetch("/users/register", options)
}
const handleChange = (event) => {
event.persist();
setValues(values => ({ ...values, [event.target.name]: event.target.value }));
};
return {
handleChange,
handleSubmit,
values,
}
};
export default useForm;
Then I have a file to manage the routes for logging in/registering in users.js
const express = require("express");
const Router = require("express-promise-router");
const db = require("../db");
const router = new Router();
//Login page
router.get('/login', (req, res) => res.send("Login"));
//Register page
router.get('/register', (req, res) => res.send("Register"));
//Register Handle
router.post('/register', (req, res) => {
console.log(req.body);
res.send('hecks');
});
module.exports = router;
I have tried messing with things inside of the handleSubmit function in my useForm.js hook, but everything leads to the console.log(req.body) from my users.js file to return as undefined. Where am I going wrong?
Edit #1: Snip from Postman sending post request
Edit #2: basic project structure
.
./client
./client/src
./client/src/components
./client/src/components/register
./client/src/components/register/register.js
./client/src/components/form
./client/src/components/form/useForm.js
./client/src/App.js
./routes
./routes/index.js
./routes/users.js
./server.js
Edit #3: Main server.js file
const express = require("express");
const mountRoutes = require("./routes");
const app = express();
mountRoutes(app);
var bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
//catch all other routes
app.get("*", function(req, res) {
res.send("<h1>Page does not exist, sorry</h1>");
});
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server started on port ${port}`));
Youβre setting state in JSON.stringify which returns undefined. youβve to pass values in it:
const handleSubmit = (event) => {
if (event) event.preventDefault();
const options = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(values)
}
fetch("/users/register", options)
}
You need to apply bodyParser before mounting routes.
So change like this:
var bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
mountRoutes(app);
You don't use then or await in the handleSubmit function which may cause problem.
Can you update the handleSubmit function like this and try?
const handleSubmit = async event => {
if (event) event.preventDefault();
const options = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(values)
};
try {
const response = await fetch("/users/register", options);
const responseData = await response.json();
if (response.ok) {
console.log("response ok");
callback();
} else {
console.log("response NOT ok");
throw new Error(responseData.message);
}
} catch (err) {
console.log(err);
if (err.response) {
console.log(err.response.data);
}
}
};
π¨βπ« You can try with this code below:
userForm.js: Make sure your handleSubmit in your userForm.js it's looks like this code below: π
const handleSubmit = async(event) => {
if (event) event.preventDefault();
const options = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(values)
}
try {
// change with your endpoint
const endpoint = 'http://localhost:3001/users/register';
const result = await fetch(endpoint, options);
// send value to your register function
callback(result);
} catch (ex) {
console.log('Something failed');
console.log(ex);
}
}
You've to use callback(result), so you can console.log that value on your register function.
express server: Make sure in your express server, you've been add body-parser, it's will looks like this code below: π
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
That code above will make your req.body works.
I hope it can help you π.
I'm trying to submit a form from Angular 7 that has text and images to a Node backend using Multer as my middleware and Express.json() as my bodyParser. The form data is there on the frontend submission and the text data is there in the backend but the image fields are empty {}. I've tried using bodyParse.json() and thre results are the same.
Here is my app.js file
const express = require('express');
// const bodyParser = require('body-parser');
const adminController = require('./controllers/admin');
const path = require('path');
const cors = require('cors')
const app = express()
const FORM_URLENCODED = 'multipart/form-data';
app.use(cors())
... my mongodb connection string ...
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Headers', 'Origin, Content-Type, X- Auth-Token')
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, DELETE, OPTIONS')
next();
});
// const bp = bodyParser.json()
// console.log('TCL: bp', bp);
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use(express.static(path.join(__dirname, 'images')));
// req is not defined?!?!?!?!?
app.use(() => {
if (req.headers['content-type'] === FORM_URLENCODED) {
let body = '';
req.on('data', chunk => {
body += chunk.toString(); // convert Buffer to string
});
req.on('end', () => {
console.log(body);
res.end('ok');
});
}
})
// -- multer
const multer = require('multer');
const crypto = require("crypto");
const imgDir = 'images';
const imgStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'images')
},
filename: function(req, file, callback) {
crypto.pseudoRandomBytes(16, function(err, raw) {
if (err) return callback(err);
callback(null, raw.toString('hex') +
path.extname(file.originalname));
});
}
});
const fileFilter = ((req, file, cb) => {
// accept image only
if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
return cb(new Error('Only image files are allowed!'), false);
}
cb(null, true);
});
const upload = multer({dest: imgDir, storage: imgStorage, fileFilter:
fileFilter, limits: {fileSize: 16000} }).array('image',20);
// app.use(
// upload.fields([
// { name: 'mainImg', maxCount: 1 },
// { name: 'image', maxCount: 5 },
// ])
// );
// -- end multer
app.post('/admin/add-product', function (req, res, next) {
var path = '';
upload({dest: imgDir, storage: imgStorage, fileFilter: fileFilter,
limits: {fileSize: 16000} });
path = req.file.path;
/// path is not defined!?!?!?!?
return res.send("Upload Completed for "+path);
}, adminController.postAddProduct);
const userRoutes = require('./routes/user');
app.use('/user', userRoutes);
module.exports = multer;
module.exports.imgStorage = imgStorage;
module.exports = app;
Angular form
<form [formGroup]="prodForm" (ngSubmit)="onSubmit()" enctype="multipart/form-data">
<div class="col-md-4">
<label for="title"> <span class="required">*</span>Title: </label>
<mat-form-field>
<input class="form-control" matInput type="text" formControlName="title" #prodTitle />
<mat-error *ngIf="prodForm.get('title').invalid">Please enter a title</mat-error>
</mat-form-field>
</div>
<div class="col-md-4">
<div class="col-md-5">
<button class="btn btn-success" mat-stroked-button type="button" (click)="filePicker.click()">
Pick Image
</button>
<input type="file" #filePicker name="image" (change)="onImagePicked($event)" />
</div>
<div class="image-preview col-md-7" *ngIf="imgSrc !== '' && imgSrc">
<img [src]="imgSrc" alt="{{ prodTitle.value }}" />
</div>
</div>
<div class="col-md-12 sectButtons">
<button class="btn btn-success" (click)="onShowStep2()">Next Step</button>
<div class="clear"></div>
</div>
Angular form output
image: File {name: "some-image.jpg", lastModified: 1552012800142,
lastModifiedDate: Thu Mar 07 2019 21:40:00 GMT-0500 (Eastern Standard Time), webkitRelativePath: "", size: 42381, β¦}
title: "some title"
Node controller output
TCL: exports.postAddProduct -> req.body { _id: '',
title: 'some title',
image: {}, }
TCL: exports.postAddProduct -> files undefined
What am I missing? I've spent way too much time trying to figure this out.
req is not defined becuase you've not defined the req object. It is not a valid express middleware. change to
// next is optional
app.use((req, res, next) => {
if (req.headers['content-type'] === FORM_URLENCODED) {
let body = '';
req.on('data', chunk => {
body += chunk.toString(); // convert Buffer to string
});
req.on('end', () => {
console.log(body);
res.end('ok');
});
}
})
I am new in node. I just want to pass value in POST request with something like uploading of file. here is my sample code:
var express = require('express');
var app = express();
var fs = require("fs");
var bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({ extended: false });
var multer = require('multer');
var upload = multer({ dest: '/tmp/'});
app.use(express.static('public'));
app.post('/process_post', urlencodedParser, function (req, res) {
console.log(req.files.file.name);
var file = __dirname + "/" + req.files.file.name;
fs.readFile( req.files.file.path, function (err, data) {
fs.writeFile(file, data, function (err) {
if( err ){
console.log( err );
}else{
response = {
message:'Save successfully',
first_name:req.body.firstname,
last_name:req.body.lastname,
filename:req.files.file.name
};
}
console.log( response );
res.end( JSON.stringify( response ) );
});
});
})
HTML:
<html>
<body>
<form action="http://127.0.0.1:8081/process_post" method="POST" enctype="multipart/form-data">
First Name: <input type="text" name="firstname">
<br>
Last Name: <input type="text" name="lastname">
<br>
Picture: <input type="file" name="file" size="50" />
<br>
<input type="submit" value="Submit">
</form>
</body></html>
The req.files is always undefine.
Thanks in advance!
You're requireing multer, but never configuring or using it. from the docs:
var multer = require('multer')
var upload = multer({ dest: 'uploads/' })
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
// req.files is array of `photos` files
// req.body will contain the text fields, if there were any
})