Node and React multer path is undefined when except of uploading image - javascript

enter image description here
enter image description here
React code
const [data, setData] = useState({
pSellingPrice: "",
pImage: "",
})
const inputImgs = useRef();
const handleSubmit = (e) => {
e.preventDefault();
let getRandomKey = Math.floor(Math.random() * 19999999);
//Insert data to database
const formData = new FormData();
formData.append("pImage", inputImgs.current.files[0]);
Object.entries(data).forEach(([key, value]) => formData.append(key, value));
formData.append("pId", getRandomKey);
formData.append("pIsActive", 1);
//Post api
axios
.post(
"http://localhost:4000/api/products/product/addProduct",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
},
{
headers: { "Content-type": "application/json" },
}
)
.then((res) => {
console.log(res)
})
.catch((err) => {
console.log(err);
});
};
****form****
<form onSubmit={(e)=>{handleSubmit(e)}}>
<input
type="number"
className="form-control"
id="formrow-inputZip"
name="pSellingPrice"
value={data.pSellingPrice}
onChange={(e) => {
handleChange(e);
}}
required
/>
<div className="col-md-6">
<div class="mb-3">
<label for="formFile" class="form-label">
Choose Product Image
</label>
<input
class="form-control"
ref={inputImgs}
type="file"
id="formFile"
name="pImage"
/>
</div>
</div>
</form>
Node code
product.Router.js
const {createProduct} = require("./Product.controller");
const router = require("express").Router();
const cors = require("cors");
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: "./image/productImage",
filename: function (req, file, cb) {
cb(null, "IMAGE-" + Date.now() + path.extname(file.originalname));
},
});
const upload = multer({
storage: storage,
}).single("pImage");
router.post("/addProduct", upload, createProduct);
module.exports = router;
product.Controller.js
const path = require("path");
const {addProduct}= = require("./product.service");
module.exports = {
createProduct: (req, res) => {
console.log(req.body);
const body = req.body;
let image = req.file.path;
console.log(image + "img");
image = image.replace("\\", "\\\\");
body.pImage = image;
console.log(body.pImage);
addProduct(body, (err, results) => {
if (err) {
console.log(err);
return res.status(500).json({
success: 0,
message: "Database connection error",
});
}
return res.status(200).json({
success: 1,
data: results,
});
});
},
}
product.service.js
const pool = require("../../../config/database");
module.exports = {
addProduct: (data, callBack) => {
console.log(data);
const query = `INSERT INTO products (pSellingPrice,pImage) VALUES
('${data.pSellingPrice}'
,'${data.pImage.slice(19)}')`;
console.log(query);
pool.query(query, (error, results, fields) => {
if (error) {
console.log(error);
return callBack(error);
}
return callBack(null, results);
});
},
}
I tried to use react with Node for uploading image to database.. I used Multer library for that purpose. It works perfectly. According to client requirement sometimes no need to insert image from form. default image should use for that when image display. I cant submit form excepting image upload. I tried few ways , but couldn't solve-out. My backend gains some path issue in product.controller.js line 25 . when it storing database automatically make path. when request is done , uploaded image will be saved in local path at backend.. Those are the resources . please someone help me to except image uploading from form submit.
Note :- Above mentioned the error message images

Related

Image not being sent in payload using Multer in Nodejs and ReactJs

I am sending an image from my reactjs frontend to my nodejs express backend using formData.
But when i am appending the image it doesn't appear in the payload and i get this error from the backend. TypeError: Cannot read properties of undefined (reading 'filename')
I'm sending the data seemingly correct, so i dont understand why the image is not being sent to the backend in the payload.
this is the payload i am getting.
Here is my code:
Backend
user.js:
const MIME_TYPES = {
'image/png': 'png',
'image/jpeg': 'jpg',
'image/jpg': 'jpg',
}
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "images");
},
filename: (req, file, cb) => {
const name = file.originalname.toLowerCase().split(" ").join("-");
const extension = MIME_TYPES[file.mimetype];
cb(null, name + "-" + Date.now() + "." + extension);
}
})
router.post("/register", multer({ storage: storage }).single("uniIDImage"), (req, res, next) => {
const url = req.protocol + "://" + req.get("host");
const fullUrl = url + "/images/" + req.file.filename;
let User = new models.User();
User.register(req.body, fullUrl).then((response) => {
console.log(response);
if(response) {
return res.json({
msg: 'User registered successfully.',
statusCode:200,
result: response,
});
}
});
});
Reactjs frontend:
registerActions.js:
export const register = (formData) => async dispatch => {
const config = {
headers: {
'Content-Type': 'multipart/form-data;',
}
}
let jsonObject = {};
for (let key of formData.keys()) {
jsonObject[key] = formData.get(key);
console.log( jsonObject[key])
}
try {
const res = await axios.post('/register', jsonObject, config);
dispatch({
type: REGISTER_SUCCESS,
payload: res.data
});
} catch (err) {
dispatch({
type:REGISTER_FAIL,
});
}
}
Register.js
import React, { useEffect, useReducer, useState } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { register } from '../../store/auth/register/actions'
const Register = ({ register }) => {
const [uniIDImage, setUniIDImage] = useState([]);
const [data, setData] = useState({
name: '',
uniID: '',
username: '',
email: '',
phoneNumber: '',
password: '',
});
const handleChange = (e) => {
setData({ ...data, [e.target.name]: e.target.value });
}
const handleImage = (e) => {
setUniIDImage({ pic: e.target.files[0] });
}
const signUp = (e) => {
e.preventDefault();
const formData = new FormData()
formData.append("name", data.name);
formData.append("uniID", data.uniID);
formData.append("username", data.username);
formData.append("email", data.email);
formData.append("phoneNumber", data.phoneNumber);
formData.append("uniIDImage", uniIDImage.pic);
formData.append("password", data.password);
console.log(uniIDImage.pic);
register(formData);
}
return (
<div className="mb-3">
<Label className="form-label">ID Photo</Label>
<Input
id="fileInput"
name="uniIDImage"
className="form-control"
accept="image/*"
type="file"
onChange={handleImage}
/>
)
Register.propTypes = {
register: PropTypes.func.isRequired,
}
export default connect(null, { register })(Register);
When you convert form data to JSON object, you lose the file, and there is no point converting it, and also no need to set content-type header.
So, simply pass formData to axios as a second argument:
export const register = (formData) => async dispatch => {
try {
const res = await axios.post('/register', formData);
dispatch({
type: REGISTER_SUCCESS,
payload: res.data
});
} catch (err) {
dispatch({
type: REGISTER_FAIL,
});
}
}

MERN - Getting error: "Cannot read property 'path' of undefined" when trying to upload a pdf file using multer

Like I said on the title, I'm facing that error when I'm trying to upload a file from the client-side. Here are the code blocks for more context:
Route:
const express = require("express");
const { upload } = require("../middlewares/uploadMiddleware");
const { registerResearchSheet } = require("../controllers/researchSheetControllers");
const router = express.Router();
router.route("/").post(upload.single("file"), registerResearchSheet);
Middleware:
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "backend/uploads/");
},
filename: function (req, file, cb) {
cb(
null,
new Date().toISOString().replace(/:/g, "-") + "-" + file.originalname
);
},
});
const fileFilter = (req, file, cb) => {
if (file.mimetype === "application/pdf") {
cb(null, true);
} else {
cb(null, false);
}
};
const upload = multer({ storage: storage, fileFilter: fileFilter });
module.exports = { upload };
Controller:
const asyncHandler = require("express-async-handler");
const ResearchSheet = require("../models/researchSheetModel");
const registerResearchSheet = asyncHandler(async (req, res) => {
const {
title,
director,
coordinator,
students,
reviewers,
proposalType,
investigationType,
status,
} = req.body;
if (!req.file) {
res.status(400);
throw new Error("The file hasn't be uploaded correctly"); **//This is the triggering error**
}
const file = req.file.path;
const researchSheetExists = await ResearchSheet.findOne({ title, students });
if (researchSheetExists) {
res.status(400);
throw new Error("Title or students can't be repeated");
}
const researchSheet = await ResearchSheet.create({
title,
file, // file: buffer,
director,
coordinator,
students,
reviewers,
proposalType,
investigationType,
status,
});
if (researchSheet) {
res.status(201).json({
_id: researchSheet._id,
title: researchSheet.title,
file: researchSheet.file,
director: researchSheet.director,
coordinator: researchSheet.coordinator,
students: researchSheet.students,
reviewers: researchSheet.reviewers,
proposalType: researchSheet.proposalType,
investigationType: researchSheet.investigationType,
status: researchSheet.status,
});
} else {
res.status(400);
throw new Error("An error has ocurred");
}
});
And finally the frontend part:
const [file, setFile] = useState(null);
//code..
const singleFileUpload = (e) => {
setFile(e.target.files[0]);
};
const submitHandler = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append("title", title);
formData.append("file", file);
formData.append("director", director);
formData.append("coordinator", coordinator);
formData.append("students", students);
formData.append("proposalType", proposalType);
formData.append("investigationType", investigationType);
console.log(file); //This log is correctly showing the File and its properties
if (
!title ||
!coordinator ||
!students ||
!proposalType ||
!investigationType
) {
setMessage("You must fill all the fields");
} else {
dispatch(registerResearchSheetAction(formData));
// history.push("/listresearchsheets");
}
};
//more code...
<Form.Control
className="mb-3"
type="file"
accept=".pdf"
onChange={(e) => singleFileUpload(e)}
/>
<Button type="submit">Register</Button>
Action:
export const registerResearchSheetAction =
(
title,
file,
director,
coordinator,
students,
reviewers,
proposalType,
investigationType,
status
) =>
async (dispatch) => {
try {
dispatch({
type: RESEARCHSHEET_REGISTER_REQUEST,
});
const formData = new FormData();
formData.append("file", file);
formData.append("title", title);
formData.append("director", director);
formData.append("coordinator", coordinator);
formData.append("students", students);
formData.append("reviewers", reviewers);
formData.append("proposalType", proposalType);
formData.append("investigationType", investigationType);
formData.append("status", status);
const config = {
headers: {
"Content-Type": "multipart/form-data",
},
};
const { data } = await axios.post(
"/api/researchsheets",
formData,
config
);
dispatch({ type: RESEARCHSHEET_REGISTER_SUCCESS, payload: data });
} catch (error) {
dispatch({
type: RESEARCHSHEET_REGISTER_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
I already tested the functionality on the server-side using Postman and it is uploading, more than the file, the route/path of it successfully on the database. That's why I'm thinking that the problem must be on the client-side. I really don't know what I'm missing here, so that's why I came for a little of help, I hope I was clear on my explanaiton.
Thank you in advance!

Multer doesn't upload my Image to my Backend

I try to implement multer to upload files for my galery.
Unfortunately it doesn't work.
Network shows me this error:
msg: "Cannot read properties of undefined (reading 'filename')"
I guess the error is somewhere here:
upload.single('galery')
Bcs i try to console.log my file insite the storage function and it seems like it's not used at all. I couldn't even print a "Hello World" in there atm.
That's why I'm getting an error after:
const image = req.file.filename;
const router = require('express').Router();
const galeryCtrl = require('../controllers/galeryCtrl');
const multer = require('multer');
const shortid = require('shortid');
const path = require('path');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '../images');
},
filename: function (req, file, cb) {
console.log(file);
cb(
null,
shortid.generate() + '-' + Date.now() + path.extname(file.originalname)
);
},
});
const fileFilter = (req, file, cb) => {
const allowedFileTypes = ['image/jpeg', 'image/jpg', 'image/png'];
if (allowedFileTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(null, false);
}
};
let upload = multer({ storage: storage, fileFilter });
router
.route('/galery')
.get(galeryCtrl.getGaleries)
.post(upload.single('galery'), async (req, res) => {
try {
const image = req.file.filename;
const galeryCheck = await Galery.findOne({ image });
// Check if already exist
if (galeryCheck)
return res.status(400).json({ msg: 'already exists' });
const newGalery = new Galery({ image, name: shortid.generate() });
await newGalery.save();
res.json({ msg: 'success' });
} catch (err) {
return res.status(500).json({ msg: err.message });
}
});
const onFileChange = (e) => {
setGalery(e.target.files[0]);
};
const onSubmit = (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('galery', galery);
console.log(formData);
axios({
url: '/api/galery',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
data: formData,
})
.then((response) => {
return response;
})
.catch((error) => {
console.log(error);
});
};
<form onSubmit={onSubmit}>
<input type='file' onChange={onFileChange} />
<button type='submit'>Submit</button>
</form>
I tried to change the code but this brings new errors.
First i was need to remove the value={galery} from my input, otherwise I am getting a white screen.
But now I've new errors:
POST http://localhost:3000/api/galery 500 (Internal Server Error)
Error: Request failed with status code 500 (from the console.log(error)
Backend code looks good to me but while sending the file in the request can you try like below.
onFileChange(e) {
setGalery(e.target.files[0])
}
onSubmit(e) {
e.preventDefault()
const formData = new FormData()
formData.append('galery', galery)
axios.post("serverurlhere", formData, {
}).then(res => {
console.log(res)
})
}
// OR ANOTHER WAY
onSubmit(e) {
e.preventDefault()
const formData = new FormData();
formData.append('galery', galery)
axios({
url:"serverurlhere",
method:'post',
headers:{
'Content-Type': 'multipart/form-data'
},
data: formData,
})
.then(response => {
return response;
})
.catch(error =>{
console.log(error);
});
}
<form onSubmit={this.onSubmit}>
<input type="file" onChange={this.onFileChange} />
<button type="submit">Submit</button>
</form>

How to populate img src with event.target.files before saving image to server

I'm saving images to a files object which I'm then using that object to save to the server.
(ReactJS)
onChange(event) {
this.setState({
file: event.target.files[0],
loaded: 0,
});
}
onClickHandler = () => {
const { match: { params} } = this.props;
const data = new FormData()
console.log(this.state.file)
data.append('file', this.state.file)
axios.post(`http://localhost:3000/albums/${params.albumId}/upload`, data, {
}).then(res => {
console.log(res.statusText)
})
}
(NodeJS - image-upload.js)
const getImageByAlbumId = (request, response) => {
const { id } = request.params;
db.pool.query('SELECT * FROM file WHERE album_id = $1 ORDER BY album_id ASC', [id], (error, results) => {
if (error) {
throw error
} else {
response.status(200).json(results.rows)
console.log("getImageByAlbumId " + JSON.stringify(request.params));
}
})
}
const imageUpload = (req, res) => {
var id = parseInt(req.params.id);
message: "Error! in image upload."
if (!req.file) {
console.log("No file recieved");
message = "Error! in image upload."
console.log("status: danger");
} else {
console.log('file recieved');
console.log(req.file);
var query = db.pool.query('INSERT INTO file (name, type, size, path, album_id) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (album_id) DO NOTHING RETURNING *', [req.file.filename, req.file.mimetype, req.file.size, req.file.path, id], (err, result) => {
console.log('inserted data')
if (err) {
console.log(err)
} else {
console.log('inserted data')
console.log(result)
}
});
message = "Successfully uploaded";
console.log("status: success");
}
}
(NodeJS - index.js)
var storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'public')
},
filename: (req, file, cb) => {
cb(null, Date.now() + '-' +file.originalname)
}
})
var upload = multer({ storage: storage });
app.use('/public', express.static('public'));
app.use(upload.single('file'));
app.get('/albums/:id/images', image.getImageByAlbumId)
app.post('/albums/:id/upload', image.imageUpload);
I'm using that file that I saved to my server which in hand saved a path to my database using req.file.path. And then adding that path to my state when componentDidMount(). I'm then trying to use that path to use for the img src.
<img style={{ width: "300px", height: "300px"}} src={this.props.file[0].path} />
My issue is that if nothing is loaded to the server yet I get this error: TypeError: Cannot read property 'path' of undefined. Also I'm trying to load the image into the src when the user clicks on that image so they can view the image on the front-end before they save it to the database and have to reload the page.
So I'm wondering how I can load that image in the src with I guess event.target.files[0] before reloading the page/ retrieving the file from the database but also using the image from the database when it's reloaded or saved to the database.
There isn't a path property on File objects. What you need to do to render the selected file is use URL.createObjectURL.
For example:
import React, { useState } from "react";
export default function App() {
const [image, setImage] = useState();
const onChange = e => setImage(URL.createObjectURL(e.target.files[0]));
return (
<div className="App">
<input type="file" onChange={onChange} />
{image && <img src={image} alt="The current file" />}
</div>
);
}
Check this CodeSandbox for an example

How to upload a single file with a title using busboy

I am trying to upload an image with it's title to Cloud Storage using react as frontend and busboy for handling the upload in the backend but I can't seem to get it working. When I submit the form I get an error saying that event.target.files[0] is undefined. Any suggestions?
React Code
handleSubmit = (event) => {
event.preventDefault();
const image = event.target.files[0];
const formData = new FormData();
formData.append('image', image, image.name);
formData.append('title', this.state.title);
this.props.addPost(formData)
};
<form onSubmit={this.handleSubmit}>
<TextField name="title" type="text" label="Title"placeholder="Add a title"/>
<input type="file" id="imageInput"/>
<Button type="submit" variant="contained" color="primary" className={classes.submitButton} disabled={loading}>
Submit
</Button>
</form>
and my API function
exports.addPost = (req,res)=> {
const BusBoy = require('busboy');
const path = require('path');
const os = require('os');
const fs = require('fs');
const busboy = new BusBoy({ headers: req.headers });
let imageToBeUploaded = {};
let imageFileName;
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
console.log(fieldname, file, filename, encoding, mimetype);
if (mimetype !== 'image/jpeg' && mimetype !== 'image/png') {
return res.status(400).json({ error: 'Wrong file type submitted' });
}
// my.image.png => ['my', 'image', 'png']
const imageExtension = filename.split('.')[filename.split('.').length - 1];
// 32756238461724837.png
imageFileName = `${Math.round(
Math.random() * 1000000000000
).toString()}.${imageExtension}`;
const filepath = path.join(os.tmpdir(), imageFileName);
imageToBeUploaded = { filepath, mimetype };
file.pipe(fs.createWriteStream(filepath));
});
busboy.on('finish', () => {
admin
.storage()
.bucket()
.upload(imageToBeUploaded.filepath, {
resumable: false,
metadata: {
metadata: {
contentType: imageToBeUploaded.mimetype
}
}
})
.then(() => {
const imgUrl = `https://firebasestorage.googleapis.com/v0/b/${
config.storageBucket
}/o/${imageFileName}?alt=media`;
const newPost = {
imgUrl: imgUrl,
userHandle: req.user.handle,
uniName: req.user.uniName,
title: req.body.title,
createdAt: new Date().toISOString(),
likeCount:0,
commentCount:0
};
db.collection('posts').add(newPost).then((doc) => {
const resPost = newPost;
resPost.postId = doc.id;
res.json(resPost);
})
})
.then(() => {
return res.json({ message: 'image uploaded successfully' });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: 'something went wrong' });
});
});
busboy.end(req.rawBody);
};
You've tied the handleSubmit to the form, but then do this in the code:
handleSubmit = (event) => {
event.preventDefault();
const image = event.target.files[0];
Since the handler is tied to the form, event.target refers to the form. And a form doesn't have a files property, hence the error message.
You'll need to look up the input and call files[0] on that.
In React you typically look up a field by defining a ref property in the field:
<input type="file" id="imageInput" ref="imageInput" />
And then in your code:
const input = this.refs.imageInput;
const image = imageInput.files[0];

Categories

Resources