Trouble with file upload to database (backend) - node.js

I'm trying to upload files to my database but I'm having trouble.
When I try to upload a file to my database I got this error :
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
And the data from my file is not save to my database (see the image) :
I think my problem is here :
const fileData = {
type: req.body.type,
name: req.body.name,
data: req.body.data
};
I tried to copy the function I use for create a user when he register but for a file when someone upload it.
Register (it works) :
users.post("/register", (req, res) => {
const today = new Date();
const userData = {
first_name: req.body.first_name,
last_name: req.body.last_name,
email: req.body.email,
password: req.body.password,
phone: req.body.phone,
deliveryAddress: req.body.deliveryAddress,
created: today
};
User.findOne({
where: {
email: req.body.email
}
})
//TODO bcrypt
.then(user => {
if (!user) {
bcrypt.hash(req.body.password, 10, (err, hash) => {
userData.password = hash;
User.create(userData)
.then(user => {
res.json({ status: user.email + " registered!" });
})
.catch(err => {
res.send("error: " + err);
});
});
} else {
res.status(400).json({ error: "Email already taken" });
console.log("Email already taken !");
}
})
.catch(err => {
res.status(400).json("Error : " + err);
});
});
File upload (not working) :
app.post("/files", (req, res) => {
const uploadFile = req.files.file;
const fileName = req.files.file.name;
const fileData = {
type: req.body.type,
name: req.body.name,
data: req.body.data
};
uploadFile.mv(`./uploads/${fileName}`, function(err) {
if (err) {
return res.status(500).send(err);
}
res.json({
file: `uploads/${fileName}`
});
});
Upload.findOne({
where: {
name: req.body.name
}
})
.then(file => {
if (!file) {
Upload.create(fileData)
.then(file => {
res.json({ status: file.name + " created !" });
})
.catch(err => {
res.send("error: " + err);
});
} else {
res.status(400).json({ error: "File already uploaded" });
console.log("File already uploaded");
}
})
.catch(err => {
res.status(400).json("Error : " + err);
});
});
I'm not very familiar with backend so...
I tried to change :
const fileData = {
type: req.body.type,
name: req.body.name,
data: req.body.data
};
with
const fileData = {
type: req.files.file.type,
name: req.files.file.name,
data: req.files.file.data
};
But I got an infinite loop and the file is not uploaded to my database (nothing is created).
The upload to the backend (uploads folder) works.
EDIT
When I use req.files.file.something for the fileData it works sometimes, the file is correctly uploaded to database but I got the error again (I think it works when the file is very tiny).
For a 1 Ko file :
Executing (default): INSERT INTO `files` (`id`,`name`,`data`,`createdAt`,`updatedAt`) VALUES (DEFAULT,'a suprimer.html',X'3c21444f43545950452068746d6c3e0d0a0d0a3c21444f43545950452068746d6c3e0d0a3c68746d6c3e0d0a3c686561643e200d0a093c6d65746120636861727365743d227574662d38223e0d0a093c7469746c653e20466169726520756e6520696d6167653c2f7469746c653e0d0a3c2f686561643e0d0a3c626f64793e0d0a0d0a093c703e746573743c2f703e0d0a0d0a093c696d67207372633d2268642e696d6167652e736e6f772e6a706567223e0d0a090d0a0d0a3c2f626f64793e0d0a3c2f68746d6c3e','2020-01-29 10:07:28','2020-01-29 10:07:28');
And in the database :
Why the type is not set up ?
How can I reduce the time of the upload for a bigger file ?

You can try this code below:
app.post("/files", (req, res) => {
const uploadFile = req.files.file;
const fileName = req.files.file.name;
const fileData = {
type: req.body.type,
name: req.body.name,
data: req.body.data
};
Upload.findOne({
where: {
name: req.body.name
}
}).then(file => {
if (!file) {
// upload file to directory
uploadFile.mv(`./uploads/${fileName}`);
// save file to database
Upload.create(fileData)
.then(file => {
return res.json({ status: file.name + " created !" });
}).catch(err => {
return res.send("error: " + err);
});
} else {
return res.status(400).json({ error: "File already uploaded" });
}
})
.catch(err => {
return res.status(400).json("Error : " + err);
});
});
I hope it can help you to upload your file.

I believe this is because you are trying to send two responses in the same call handler. Once in uploadfile.mv then again in Upload.findOne. You cannot return two res.X to the same request.
This thread might be useful: Error: Can't set headers after they are sent to the client

Related

keeps getting "Illegal arguments: undefined, string at Object.bcrypt.hashSync"

I've been struggling with Bcrypt on my MERN project I'm trying to create an authentication system I'm trying to run tests on Postman and I'm not sure why do I keep getting the error: "Illegal arguments: undefined, string at Object.bcrypt.hashSync"
this is my postman request:
this is the Controller Code:
const config = require("../config/auth.config");
const db = require("../models");
const User = db.user;
const Role = db.role;
var jwt = require("jsonwebtoken");
var bcrypt = require("bcryptjs");
exports.signup = (req, res) => {
const user = new User({
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password, 8),
});
user.save((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
if (req.body.roles) {
Role.find(
{
name: { $in: req.body.roles },
},
(err, roles) => {
if (err) {
res.status(500).send({ message: err });
return;
}
user.roles = roles.map((role) => role._id);
user.save((err) => {
if (err) {
res.status(500).send({ message: err });
return;
}
res.send({ message: "User was registered successfully!" });
});
}
);
} else {
Role.findOne({ name: "user" }, (err, role) => {
if (err) {
res.status(500).send({ message: err });
return;
}
user.roles = [role._id];
user.save((err) => {
if (err) {
res.status(500).send({ message: err });
return;
}
res.send({ message: "User was registered successfully!" });
});
});
}
});
};
exports.signin = (req, res) => {
User.findOne({
username: req.body.username,
})
.populate("roles", "-__v")
.exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
if (!user) {
return res.status(404).send({ message: "User Not found." });
}
var passwordIsValid = bcrypt.compareSync(
req.body.password,
user.password
);
if (!passwordIsValid) {
return res.status(401).send({ message: "Invalid Password!" });
}
var token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 86400, // 24 hours
});
var authorities = [];
for (let i = 0; i < user.roles.length; i++) {
authorities.push("ROLE_" + user.roles[i].name.toUpperCase());
}
req.session.token = token;
res.status(200).send({
id: user._id,
username: user.username,
email: user.email,
roles: authorities,
});
});
};
exports.signout = async (req, res) => {
try {
req.session = null;
return res.status(200).send({ message: "You've been signed out!" });
} catch (err) {
this.next(err);
}
};
The error message:
Illegal arguments: undefined, string at Object.bcrypt.hashSync wants to say that you're passing undefined as an argument to the hashSync function. We need to fix this error.
Take a closer look at this line where the error occurs:
password: bcrypt.hashSync(req.body.password, 8),
req.body.password is undefined, you can verify it by console.log(req.body.password). What's wrong is that you are sending data as URL parameters. So req.body is an empty object and req.body.password is undefined.
In Postman, select the Body tab, choose JSON format, then type your data as a JSON object. Then, in your code, use express.json() middleware to parse requests in JSON format. You'll have the desired output.
You can see my example request in Postman below:

Node JS path format

export const registerUser = expressAsyncHandler(async (req, res) => {
const { name, email, password, isAdmin, role } = req.body;
const userExist = await User.findOne({ email });
if (userExist) {
//Rollback if we get a error
if (req.file) {
fs.unlink(req.file.path, (error) => {
console.log("File Deleted");
});
}
res.status(400);
throw new Error("User already Exist");
}
const user = await User.create({
image: "http://localhost:5000/" + req.file.path,
name,
email,
password,
isAdmin,
role,
});
if (user) {
res.status(201);
res.json({
_id: user._id,
image: user.image,
name: user.name,
email: user.email,
isAdmin: user.isAdmin,
token: generateWebToken(user._id),
});
} else {
if (req.file) {
fs.unlink(req.file.path, (error) => {
console.log("File Deleted");
});
}
res.status(400);
throw new Error("Invalid User Data");
}
});
I store image on my local storage and now i want to access it the problem is when i try to append my local host address and file path address i get localhost:5000/user\uploads\images.
i want localhost:5000/user/uploads/images
i try to replace \ with / but i can't able to do that using replace method.

How to validate data from csv file in nodejs?

My CSV file contains 3 columns: first name,last name,email. I want to add all the valid lines of the CSV file in the users table in the database. For example: if the CSV file has 10 lines where line 2 is missing first name and line 3 has invalid email, I want to see 8 users added to database. After adding the users from the CSV file, it should display how many users were added and how many failed.
My model:
const User = sequelize.define(
"users",
{
first_name: {
type: Sequelize.STRING(60),
allowNull: false,
},
last_name: {
type: Sequelize.STRING(60),
allowNull: false,
},
email: {
type: Sequelize.STRING(255),
allowNull: false,
},
My Controller:
const upload = async (req, res) => {
try {
if (req.file == undefined) {
return res.status(400).send("Please upload a CSV file!");
}
let users = [];
console.log(" __basedir", __basedir);
let path = __basedir + "/uploads/" + req.file.filename;
fs.createReadStream(path)
.pipe(csv.parse({ headers: true }))
.on("error", (error) => {
throw error.message;
})
.on("data", (row) => {
users.push(row);
})
.on("end", () => {
User.bulkCreate(users, {
validate: true,
})
.then(() => {
res.status(200).send({
message:
`Uploaded ${users.length} data successfully from ` + req.file.originalname,
});
})
.catch((error) => {
res.status(500).send({
message: `Fail to import ${users.length} into database!`,
error: error.message,
});
});
});
} catch (error) {
res.status(500).send({
message: "Could not upload the file: " + req.file.originalname,
});
}
};
How can I solve this?
I solve it using fast-csv package
import * as csv from 'fast-csv';
const upload = async (req, res) => {
try {
if (req.file == undefined) {
return res.status(400).send("Please upload a CSV file!");
}
let users = [];
console.log(" __basedir", __basedir);
let path = __basedir + "/uploads/" + req.file.filename;
console.log("path", path);
let emailPattern= /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i;
let successCount = 0;
let errorCount = 0;
fs.createReadStream(path)
.pipe(csv.parse({ headers: [ undefined,'first_name', 'last_name', 'email', undefined ],renameHeaders: true, ignoreEmpty: true }))
.validate(data => data.first_name !== '' && data.last_name !== '' && emailPattern.test(data.email))
.on("error", (error) => {
throw error.message;
})
.on("data", (row) => {
successCount++;
users.push(row);
console.log(`ROW=${JSON.stringify(row)}`)
})
.on('data-invalid', (row, rowNumber) => {
errorCount++;
console.log(`Invalid [rowNumber=${rowNumber}] [row=${JSON.stringify(row)}]`)
})
.on("end", (rowCount) => {
console.log(`Parsed ${rowCount} rows`)
User.bulkCreate(users, {
validate: true,
})
.then(async() => {
res.status(200).json({
error: false,
success:
`Uploaded ${successCount} row successfully from ` + req.file.originalname,
failed:`Uploaded ${errorCount} row failed from ` + req.file.originalname,
});
})
.catch((error) => {
console.log(error);
res.status(500).json({
error: error.message,
failed: `Fail to import ${users.length} row into database!`,
});
});
});
} catch (error) {
console.log(error);
console.log(error.message);
res.status(500).json({
error: error.message,
failed: "Could not upload the file: " + req.file.originalname,
});
}
};

How to get response from nodejs server

In my application I want to get response from nodejs server. if I enter already registered mail id I want to get "User already exists" message on browser console.log in register.component.ts. How do it?
Many times tried but not able to findout. Please anyone help.
user.js://server
users.post('/register', (req, res) => {
const today = new Date()
const userData = {
first_name: req.body.first_name,
last_name: req.body.last_name,
email: req.body.email,
password: req.body.password,
created: today
}
User.findOne({
email: req.body.email
})
//TODO bcrypt
.then(user => {
if (!user) {
User.create(userData)
.then(user => {
const payload = {
_id: user._id,
first_name: user.first_name,
last_name: user.last_name,
email: user.email
}
let token = jwt.sign(payload, process.env.SECRET_KEY, {
expiresIn: 1440
})
res.json({ token: token })
})
.catch(err => {
res.send('error: ' + err)
})
} else {
res.json({ error: 'User already exists' })
}
})
.catch(err => {
res.send('error: ' + err)
})
})
authentication.service.ts:
public register(user: TokenPayload): Observable<any> {
const base = this.http.post(`/users/register`, user)
const request = base.pipe(
map((data: TokenResponse) => {
if (data.token) {
this.saveToken(data.token)
}
return data;
})
)
return request;
}
register.component.ts:
register() {
this.auth.register(this.credentials).subscribe(
() => {
this.router.navigateByUrl('/profile')
},
err => {
console.error(err); // how to get error message like "User already exits"
}
)
}

node.js can't get some data

I tried to build a simple web with express.
I use postman to post some data in mysql.
But when I tried to use sql query the data showed undefined.
Here's my code.
login.js
const db = require('./connection_db');
module.exports = function memberLogin(memberData) {
let result = {};
return new Promise((resolve, reject) => {
db.query('SELECT * FROM member_info WHERE email = ? AND password = ?', [memberData.email, memberData.password], function (err, rows) {
if (err) {
result.status = "fail to login"
result.err = "please try again later"
reject(result);
return;
}
resolve(rows);
});
});
}
part of controller.js
const memberData = {
name: req.body.name,
email: req.body.email,
password: req.body.password,
birthday: req.body.birthday
}
loginAction(memberData).then(rows => {
if (check.checkNull(rows) === true) {
res.json({
result: {
status: "fail to login",
err: "please try again"
}
})
} else if (check.checkNull(rows) === false) {
res.json({
result: {
status: "success",
loginMember: "welcome " + rows[0].name + " login!",
}
})
}
})

Resources