File uploading with Express: req.files is undefined - node.js

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) => {})

Related

React Nodejs Express Server Cannot GET

I'm developing my first full-stack project. I developed the client side and server side. You can see the code down. When I run the client (Front end side ) it is working in localhost. Then when I ran the backend side I got a 'Cannot GET' message. Any idea about this?
Index.js
const express = require("express");
const app = express();
const dotenv = require("dotenv");
const mongoose = require("mongoose");
const authRoute = require("./routes/auth");
const userRoute = require("./routes/users");
const postRoute = require("./routes/posts");
const categoryRoute = require("./routes/categories");
const multer = require("multer");
const path = require("path");
dotenv.config();
app.use(express.json());
app.use("/images", express.static(path.join(__dirname, "/images")));
mongoose.connect(process.env.MONGO_URL).then(console.log('Connected to mongo')).catch(err=>console.log(err));
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "images");
},
filename: (req, file, cb) => {
cb(null, req.body.name);
},
});
const upload = multer({ storage: storage });
app.post("/api/upload", upload.single("file"), (req, res) => {
res.status(200).json("File has been uploaded");
});
app.use("/api/auth", authRoute);
app.use("/api/users", userRoute);
app.use("/api/posts", postRoute);
app.use("/api/categories", categoryRoute);
app.listen("3000", () => {
console.log("Backend is running.");
});
Home.jsx
import React from 'react'
import Header from '../../components/header/Header'
import Posts from '../../components/posts/Posts'
import Sidebar from '../../components/sidebar/Sidebar'
import axios from "axios";
import { useEffect, useState } from "react";
export default function Home() {
const [posts, setPosts] = useState([]);
useEffect(() => {
const fetchPosts = async () => {
const res = await axios.get("/posts");
console.log(res)
setPosts(res.data);
};
fetchPosts();
});
return (
<>
<div className="mt-8">
<Header/>
</div>
<div className="container">
<div className="row mt-5">
<div className="col-lg-4 col-md-4">
<Posts posts={posts}/>
</div>
<div className="col-lg-4 col-md-4 ms-auto">
<Sidebar/>
</div>
</div>
</div>
</>
)
}
I added proxy to package.json file. (Client side).
"proxy": "http://localhost:3000/api/",

Having Trouble posting data to api in react

I am following a mern stack tutorial and trying to send data to api from react js form which is:
import axios from 'axios'
import { useState } from 'react'
import './share.css'
export default function Share() {
const [username, setUsername] = useState('')
const [file, setFile] = useState(null)
const handleSubmit = async(e)=>{
e.preventDefault()
const newPost = {
username: username
}
if(file){
const data = new FormData()
const fileName = file.name
data.append('name', fileName)
data.append('file', file)
newPost.pic = fileName
console.log(newPost)
try{
await axios.post('http://localhost:5000/upload', data)
}catch(err){
console.log(err)
}
}
try{
await axios.post('http://localhost:5000/post/post', newPost)
}catch(err){
console.log(err)
}
}
return (
<form className='share' onSubmit={handleSubmit}>
<div className='shareTop'>
<span>M</span>
<input className='shareInput' type='text' placeholder="What's happening?" onChange={(e)=>setUsername(e.target.value) } />
</div>
<div className='shareBottom'>
<div className='bottomIcons'>
<label htmlFor='file'>
<i class="fa-solid fa-face-meh"></i>
<input type='file' style={{display:"none"}} name='file' id='file' onChange={(e)=>setFile(e.target.files[0])} />
</label>
<i class="fa-solid fa-calendar"></i>
<i class="fa-solid fa-notes"></i>
<i class="fa-solid fa-bookmark"></i>
</div>
<button type='submit'>Tweet</button>
</div>
</form>
)
}
and my index js (serve side) file is:
const express = require('express')
const app = express()
const mongoose = require('mongoose')
const helmet = require('helmet')
const morgan = require('morgan')
const postRoute = require('./routes/posts')
const multer = require('multer')
const bodyParser = require('body-parser')
const cors = require('cors')
app.use(cors())
app.use(express.json())
app.use(morgan('common'))
app.use(helmet())
app.use(express.static('uploads'))
const storage = multer.diskStorage({
destination: function(req, file, cb){
cb(null, './uploads')
},
filename: function(req, file, cb){
cb(null, req.body.name)
}
})
const upload = multer({storage});
app.post('/upload', upload.single('file'), (req, res)=>{
})
app.use('/post/', postRoute)
mongoose.connect('mongodb://localhost:27017/twitter').then(()=>{
console.log("Connected to Database")
})
app.listen(5000, ()=>{
console.log('Listening')
})
and posts route is:
const express = require('express')
const router = express.Router()
const Post = require('../models/Post')
router.post('/post', async(req, res)=>{
const post = new Post({
username: req.body.username,
pic: req.body.pic
})
try{
const newPost = await post.save()
console.log("post has been saved")
res.status(200).json("Post has been saved")
}catch(err){
res.status(500).json(err)
}
})
router.put('/:id', async(req, res)=>{
const post =await Post.findByIdAndUpdate(req.params.id, {$set:req.body})
res.status(200).json("Account has been updated")
})
router.get('/', async (req, res)=>{
const posts = await Post.find()
res.status(200).json(posts)
})
router.delete('/', async(req, res)=>{
await Post.deleteMany()
})
module.exports = router
So the problem is that the file is being uploaded to backend but the data does not, after doing some research i realized that the second try and catch block where i am posting the content is not being called, just the first api where we upload file is being called, can someone tell me what am i doing?

Uploading Images using Multer (works locally, but not when deployed on Heroku)

I'm a bit stuck with my blog app.
It works properly when I run it locally, but when it's deployed to Heroku it seems that the images that get uploaded via Multer aren't being uploaded into my images folder
When I create a post from the Heroku app, I can create the post - but the picture is missing
Below is my server code
const path = require("path");
const db = require('./config/connection');
const multer = require('multer')
const routes = require('./routes');
const app = express()
const PORT = process.env.PORT || 5000
app.use(express.urlencoded({ extended: true }));
app.use(express.json())
app.use("/images", express.static(path.join(__dirname, "./images")));
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, 'images')
},
filename: (req, file, callback) => {
callback(null, req.body.name)
}
})
const upload = multer({ storage: storage })
app.post('/upload', upload.single('file'), (req, res) => {
res.status(200).json('File has been uploaded')
})
if (process.env.NODE_ENV === 'production') {
app.use(express.static("client/build"));
}
app.use(routes)
db.once('open', () => {
app.listen(PORT, () => {
console.log(`API server running on port ${PORT}!`);
});
});
Below here is my client code
import React, { useContext, useState } from 'react';
import './write.css';
import { Context } from '../../context/Context';
export default function Write() {
const [title, setTitle] = useState('')
const [description, setDescription] = useState('')
const [file, setFile] = useState('')
const { user } = useContext(Context)
const handleSubmit = async (e) => {
e.preventDefault()
const newPost = {
username: user.username,
title,
description,
}
if(file){
const data = new FormData()
const filename = Date.now() + file.name
data.append('name', filename)
data.append('file', file)
newPost.postPicture = filename
try {
await axios.post('/upload', data)
} catch (error) {
console.log(error)
}
}
try {
const response = await axios.post('/posts', newPost)
window.location.replace('/posts/'+response.data._id)
} catch (error) {
console.log(error)
}
}
return (
<div className='write'>
{file &&
<img
className='write_image'
src={URL.createObjectURL(file)}
alt=""
/>
}
<form className='write_form' onSubmit={handleSubmit}>
<div className='write_form_group'>
<label htmlFor='file_input'>
<i className='write_icon far fa-plus-square'></i>
</label>
<input type='file' id='file_input' style={{display: 'none'}} onChange={e=>setFile(e.target.files[0])} />
<input type='text' placeholder='Title' className='write_input' onChange={e=>setTitle(e.target.value)}/>
</div>
<div className="write_form_group">
<textarea placeholder='Tell your story...' type='text' className='write_input write_text' onChange={e=>setDescription(e.target.value)}></textarea>
</div>
<button className='write_submit' type='submit'>Publish</button>
</form>
</div>
)
}
I'm not sure what's going wrong, my brain is being pointed into the server side code where I use multer.diskStorage (maybe this doesn't work when the app is being hosted somewhere?)
Any advice would be amazing
Also here is the link to the whole repo on my github... because I feel like just looking at the code in these posts might be hard to follow
https://github.com/Wickette/wickettes_blog
Heroku doesn't let you do this. You need to create a bucket on Amazon's S3 service or another similar service to upload that.
You may also use cloudinary its easy to use also a free service.

uploading image from react frontend to express backend and displaying it back in the frontend

All I want to achieve is to upload the image in react frontend, save it in local public/images folder in backend and display the uploaded images back in frontend. So far I have managed to upload the files to backend. I cant figure out how to display the files in frontend. I want to make a get request to send the image files to frontend
express code:
express code image
const express = require("express");
const multer = require("multer");
const cors = require("cors");
const app = express();
app.use(cors());
app.use(express.static("./public"));
var storage = multer.diskStorage({
destination: "./public/images",
filename: function (req, file, cb) {
cb(null, Date.now() + '-' +file.originalname )
}
})
var upload = multer({ storage: storage }).array('file');
app.post('/upload',function(req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
return res.status(500).json(err)
} else if (err) {
return res.status(500).json(err)
}
return res.status(200).send(req.file)
})
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, ()=> {
console.log("server started at port "+PORT);
});
React frontend code:
import React, { useState } from 'react';
import axios from "axios";
const ImageForm = () => {
const [file, setFile] = useState(null);
const handleFileChange = (event) => {
setFile(event.target.files);
console.log(file)
}
const handleSubmit = (event) => {
event.preventDefault();
const data = new FormData();
for(var x = 0; x<file.length; x++) {
data.append('file', file[x])
}
axios.post("http://localhost:5000/upload", data)
.then(res => {
console.log(res.statusText)
})
}
return (
<div>
<form >
<div className="form-group" >
<label htmlFor="file">Upload File:</label>
<input
className="form-control-file mb-3"
type="file" id="file"
accept=".jpg"
multiple
onChange={handleFileChange}
/>
<button
className="btn btn-primary mt-3"
onClick={handleSubmit}
>Upload</button>
</div>
</form>
{/* Display Image Here */}
</div>
);
}
export default ImageForm;
res: response
error: err
Firstly, create a state:
const [imgFile, setImgFile] = useState('');
Then, replace the following part of your code:
axios.post("http://localhost:5000/upload", data)
.then(res => {
setImgFile('http://localhost:5000/public/images/'+res.data.filename)
})
Add this after the form tag
<img src={imgFile} alt="img"/>
Just in case the image does not load, add this in the express code:
app.use('/public/images', express.static(__dirname + '/public/images/'));

REACT : How can we Upload Image and Input text using Fetch or Axios in a single click function

So basically I'm new to react and couldn't find a single doc or resources regarding uploading an Image and input value at the same time.
One Solution is Form Data, But that isn't working as expected
The other method is Serialize, But I can't find any doc that explain the way to use in React
So, it would be really great for me and the newbies of React to know about this if you anyone could help me out.
You can try the following method you can use multer with express to handle the file data that is been uploaded.
React file
import React, { Component } from "react";
import ReactDOM from "react-dom";
import axios from "axios";
class App extends Component {
handleFileChange = e => {
this.setState({ file: e.target.files[0] });
};
handleChange = e => {
this.setState({ text: e.target.value });
};
upload = () => {
if (this.state.file) {
let data = new FormData();
data.append("file", this.state.file);
data.set("data", this.state.text);
axios
.post("http://yourhost/file", data)
.then(response => console.log(response))
.catch(error => console.log(error));
}
};
render() {
return (
<div>
<input type="text" onChange={this.handleChange} />
<input type="file" onChange={this.handleFileChange} />
<input type="button" onClick={this.upload} value="Upload" />
</div>
);
}
}
export defaults App;
express server side
const express = require('express');
const app =express();
const path = require('path');
const cors = require('cors')();
const bodyParser = require('body-parser');
const multer = require('multer')
const port =process.env.PORT || 3000;;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended:true
}));
app.use(cors);
const storage = multer.diskStorage({
destination: __dirname +'/media/',
filename(req, file, cb) {
console.log(file);
cb(null, `${file.originalname}-${new Date()}`);
}
});
const upload = multer({ storage });
app.post('/file',upload.single('file'), function(req, res) {
console.log(req.body.data);
console.log(req.files);
});
app.listen(port,()=> console.log('Running on port: '+port));

Resources