Working form with file upload image in expressjs - node.js

What is the simple proper way to upload an image with the form?
I want to upload an image with the form. I have tried working it out but I got "unexpected field". I have installed multer and followed a simple tutorial but not working as expected
This is my route
var express = require('express');
const multer = require('multer');
var router = express.Router();
const multerConfig = {
storage: multer.diskStorage({
//setup where the user's file will go
destination: function(req, file, next){
next(null, './public/images');
},
//then give the file a unique name here
filename: function(reg, file, next){
console.log(file);
const ext = file.mimetype.split('/')[1];
next(null, file.fieldname + '-' + Date.now() + '.'+ext)
}
}),
//a means of ensuring only images are uploaded.
fileFilter: function(req, file, next){
if(!file){
next();
}
const image = file.mimetype.startsWith('image/');
if(image){
console.log('photo uploaded');
next(null, true);
}else{
console.log('file not supported');
return next();
}
}
}
var bookControllers = require("../controllers/bookControllers")
//const csrf = require("../app");
/* GET home page.
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
*/
router.get('/', bookControllers.index);
router.post('/addbooks', multer(multerConfig).single('photo'), bookControllers.addbooks);
router.get('/addbooks/:id/:book_url', bookControllers.vieweachbook);
//router.get('/addbooks/:id/edit', bookControllers.edit);
router.get("/:id/edit", bookControllers.edit);
router.get('/:id/delete', bookControllers.delete);
router.put("/:id/update", bookControllers.update);
module.exports = router;
This is addbooks in bookControllers.js
exports.addbooks = function (req, res)
{
const schema = Joi.object().keys({
book_name: Joi.string().trim().min(6).max(25).required(),
summaries: Joi.string().trim().required(),
isbn: Joi.number().required(),
categories: Joi.string().trim().required(),
});
Joi.validate(req.body, schema, (err, result) => {
if(err){
console.log(err)
return res.redirect("/");
}
var url = slug(req.body.categories, {lower: true});
var book_url = slug(req.body.book_name, {lower: true});
//I have to get the auth user and add to the field later
var author = "1";
//Perform knex insert into db
knex('book').insert({
book_name: req.body.book_name,
author: author,
summary: req.body.summaries,
isbn: req.body.isbn,
category: req.body.categories,
image: req.body.image,
url: url,
book_url: book_url
}).then(function(result){
console.log(err)
return res.redirect('/');
});
});
}
and this is the form
<div class="col-lg-4">
<div class="card">
<div class="card-header bg-primary"> Add Books </div>
<form id="c_form-h" class="" method="post" action="/addbooks" enctype="multipart/form-data" style="padding: 5px;">
<div class="form-group row">
<label for="addbookname" class="col-6 col-form-label">Book Name</label>
<div class="col-12">
<input type="text" class="form-control" name="book_name" placeholder="Add Book Name" required>
</div>
</div>
<div class="form-group row">
<label for="addsummary" class="col-6 col-form-label">Summary</label>
<div class="col-12">
<textarea class="form-control" name="summaries" placeholder="Write Some Description" rows="3" required>
</textarea>
</div>
</div>
<div class="form-group row">
<label for="addcategory" class="col-9 col-form-label">Select Category</label>
<div class="col-12">
<input type="number" class="form-control" name="isbn" placeholder="ISBN" required>
</div>
</div>
<div class="form-group row">
<label for="addimage" class="col-9 col-form-label">Add Image</label>
<div class="col-12">
<input type="file" class="form-control-file" name="image" required>
</div>
</div>
<div class="form-group row">
<label for="addcategory" class="col-9 col-form-label">Select Category</label>
<div class="col-12">
<select class="form-control" name="categories" required>
<option></option>
<option>Fruits</option>
<option>Intercontinental Foods</option>
<option>3</option>
<option>4</option>
</select>
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
Also, can some also give me a hint on image update? I mean How to change the image
Thanks

In your HTML you have a file name as image
<input type="file" class="form-control-file" name="image" required>
So, You need to mention the same file name on multer as image instead photo
//Replaced photo with image
router.post('/addbooks', multer(multerConfig).single('image'), bookControllers.addbooks);
Reference : Multer - API Documentation
Update Path on Database :
You will get the file information in req.file on your controller, Like
{ fieldname, originalname, encoding, mimetype, destination, filename, path, size }
Depends on your need you can save file information on your database
knex('book').insert({
...
image: req.file.path,
....
}).then(function(result){

Related

How to check if a username is taken in Mongo Schema with an AJAX API call

I am trying to create a registration form where a potential user is typing in their desired username it checks against the existing Mongo DB User Schema to see if that exact name exists.
The form I have is here:
<form class="needs-validation" action="/register" method="POST" novalidate>
<div class="text-center mb-3">
<p class="lead">Welcome to the team! We just need a few things to get started.</p>
<div class="row">
<div class="col-md-6 mb-4">
<div class="form-outline">
<input
type="text"
id="first_name"
name="first_name"
class="form-control"
required
/>
<label class="form-label" for="first_name"
>First name</label
>
<div class="valid-feedback">Looks good!</div>
<div class="invalid-feedback">Thats not right</div>
</div>
</div>
<div class="col-md-6 mb-4">
<div class="form-outline">
<input
type="text"
id="last-name"
name="last-name"
class="form-control"
required
/>
<label class="form-label" for="last-name"
>Last name</label
>
<div class="valid-feedback">Looks good!</div>
<div class="invalid-feedback">Thats not right</div>
</div>
</div>
</div>
<!-- Email input -->
<div class="form-outline mb-4">
<input
type="email"
id="register_email"
name="register_email"
class="form-control"
required
/>
<label class="form-label" for="register_email">Email</label>
<div class="valid-feedback">Looks good!</div>
<div class="invalid-feedback">Thats not right</div>
</div>
<div class="text-center mb-3">
<div class="form-outline mb-4">
<input type="text" id="register_username" name="register_username" class="form-control" placeholder="enter your username" required />
<label class="form-label" for="register_username">Username</label>
<div id="username-check"></div>
</div>
<!-- Password input -->
<div class="form-outline mb-4">
<input type="password" id="register_password" name="register_password" class="form-control" required/>
<label class="form-label" for="register_password">Password</label>
</div>
</div>
<!-- Submit button -->
<div class="text-center">
<button type="submit" id="register" class="btn btn-lg btn-rounded bg-insider link-dark mb-3 fw-bold" disabled>
Lets Go! <i class="fa-solid fa-gauge-max"></i>
</button>
</div>
</form>
I have this script code working on the page to take the user input and check to a get route that should be checking my MongoDB:
$('#register_username').on('keyup', function () {
$.get('/usercheck?username=' + $(this).val().toLowerCase(), function (response) {
$('#username-check').text(response.message);
console.log('')
var btn = document.getElementById('register');
btn.disabled = true;
if ($('#username-check').html() === "user exists") {
$('#username-check').text('username not available').css('color', 'red');
}
else {
console.log($('#register_username').val())
$('#username-check').text('username is available').css('color', 'green');
btn.disabled = false;
}
})
});
This is the route it calls to check the database:
var express = require("express"),
router = express.Router(),
passport = require("passport"),
User = require("../models/user");
router.get('/usercheck', function(req, res) {
console.log(req.query);
User.findOne({username: req.query.register_username}, function(err, username){
if(err) {
console.log(err);
}
var message;
if(username) {
console.log(username);
message = "user exists";
console.log(message);
} else {
message= "user doesn't exist";
console.log(message);
}
res.json({message: message});
});
});
module.exports = router;
In case this helps, this is the user Schema in the database:
var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var UserSchema = new mongoose.Schema({
username: String,
password: String,
email: String,
isAdmin: { type: Boolean, default: false }
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User",UserSchema);
I am not sure what I am missing but anything would be extremely helpful. Thanks in advance!
Just so this question can be closed successfully:
Changing /usercheck?username= to /usercheck?register_username= did the trick, because this query param was used in the form:
$('#register_username').on('keyup', function () {
$.get('/usercheck?register_username=' + $(this).val().toLowerCase(), function (response) {
$('#username-check').text(response.message);
console.log('')
var btn = document.getElementById('register');
btn.disabled = true;
if ($('#username-check').html() === "user exists") {
$('#username-check').text('username not available').css('color', 'red');
}
else {
console.log($('#register_username').val())
$('#username-check').text('username is available').css('color', 'green');
btn.disabled = false;
}
})
});

ExpressJs File upload Issue

Even on uploading a file with .jpg or .png format it shows
format not allowed, please upload file with '.png','.gif','.jpg'.
I don't know why it is happening. How to solve or fix this issue? I tried to use ways suggested on some other websites to solve this issue but I wasn't able to fix this issue.
routes.js:
const {
con,
sessionStore
} = require('./config/db');
exports.new = function(req, res){
message = '';
if(req.method == "POST"){
const post = req.body;
const username= post.username;
const title= post.title;
const state= post.state;
const category= post.category;
const description= post.description;
if (!req.files)
return res.status(400).send('No files were uploaded.');
const file = req.files.uploaded_image;
var img_name=file.name;
if(file.mimetype == "image/jpeg" ||file.mimetype == "image/png"||file.mimetype == "image/gif" ){
file.mv('public/imgs/uploads/'+file.name, function(err) {
var sql = "INSERT INTO `nt_data`(`username`,`title`,`state`,`category`, `images` ,`description`) VALUES ?";
var query = con.query(sql, [username, title, state, category, img_name, description], function(err) {
if (!err) {
res.redirect('show/' + username);
}
else {
message = "This format is not allowed , please upload file with '.png','.gif','.jpg'";
res.render('new.ejs',{message: message});
}
});
});
}
}
else {
res.render('new');
}
};
exports.show = function(req, res){
let message = '';
var username = req.params.username;
const sql="SELECT * FROM `nt_data` WHERE `username`='"+username+"'";
con.query(sql, function(err, result){
if(result.length <= 0)
message = "show not found!";
res.render('show.ejs',{data:result, message: message});
});
};
part of my new.ejs file
<form action="/" method="POST" role="form" enctype="multipart/form-data" class="was-validated">
<% if (message.length > 0) { %>
<div class="alert alert-success col-sm-12"><%= message %></div>
<% } %>
<div class="input-group mb-3 input-group-lg">
<div class="input-group-prepend">
<span class="input-group-text">Username</span>
</div>
<input type="text" class="form-control" required placeholder="Enter Username" name="username">
<div class="valid-feedback">Valid.</div>
<div class="invalid-feedback">Please fill out this field.</div>
</div>
<div class="input-group mb-3 input-group-lg">
<div class="input-group-prepend">
<span class="input-group-text">Title</span>
</div>
<input type="text" class="form-control" required placeholder="Enter Title" name="title">
<div class="valid-feedback">Valid.</div>
<div class="invalid-feedback">Please fill out this field.</div>
</div>
<div class="custom-file mb-3">
<input type="file" class="custom-file-input" id="customFile" name="uploaded_image">
<label class="custom-file-label" for="uploaded_image">Choose file</label>
</div>
You should use multer for image or file uploading to server. Because you must use buffer to transfer data from user to local or public server and must create session. You can do these using multer easily.
You can check
https://www.npmjs.com/package/multer

Multer and CSRF and <form action="?_method=PUT">

So, ive just added multer and cloudinary to my project.
whenever I add something to my db using my form. it gives this error
"ForbiddenError: invalid csrf token"
but i've fixed that by adding ?_csrf=<%= csrfToken %>" to my action attribute in my form
<form action="/admin?_csrf=<%= csrfToken %>" method="POST" enctype="multipart/form-data">
Now, I'm trying to do the same thing for my update route.
and it's giving me the same error from before "ForbiddenError: invalid csrf token"
I've tried doing
<form action="/admin/<%= prod._id %>?_method=PUT?_csrf=<%= csrfToken %>" method="POST" enctype="multipart/form-data">
or this
<form action="/admin/<%= prod._id %>?_method=PUT?_csrf=<%= csrfToken %>" method="POST" enctype="multipart/form-data">
or this
<form action="/admin/<%= prod._id %>?_method=PUT_csrf=<%= csrfToken %>" method="POST" enctype="multipart/form-data">
or this
<form action="/admin/<%= prod._id %>?_csrf=<%= csrfToken %>_method=PUT" method="POST" enctype="multipart/form-data">
you know... different kind of variations... hoping one would work. hahah
anyways, is what I am doing impossible? that I could have 2 methods in my action attribute?
is there another way i could do this somehow?
Edit.ejs
<section class="no-padding-top">
<div class="container-fuid">
<div class="row">
<div class="col-lg-12">
<div class="block">
<div class="title"><strong>Edit Item</strong></div>
<form action="/admin/<%= prod._id %>?_method=PUT" method="POST" enctype="multipart/form-data">
<div class="form-group row">
<label class="col-sm-3 form-control-label">Product Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="prod[name]" value="<%= prod.name %>" required>
</div>
</div>
<div class="form-group row">
<label for="image" class="col-sm-3 form-control-label">Product Image</label>
<div class="col-sm-9">
<input type="file" id="image" name="image" accept="image/*" >
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 form-control-label">Product Price</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="prod[price]" value="<%= prod.price %>" required>
</div>
<label class="col-sm-1 form-control-label">Product Quantity</label>
<div class="col-sm-2">
<input type="text" class="form-control" name="prod[quantity]" value="<%= prod.quantity %>" required>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 form-control-label">Product Brand</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="prod[brand]" value="<%= prod.brand %>" required>
</div>
<label class="col-sm-1 form-control-label">Product type</label>
<div class="col-sm-2">
<input type="text" class="form-control" name="prod[type]" placeholder="Types of produces. i.e Wheels, Lights, Steering Wheels..." value="<%= prod.type %>" required>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 form-control-label">Product Description</label>
<div class="col-sm-9">
<textarea cols="30" rows="10" class="form-control" name="prod[description]" required> <%= prod.description%> </textarea>
</div>
</div>
<div class="form-group row">
<div class="col-sm-9 ml-auto">
<button type="submit" class="btn btn-primary">Edit Item</button>
Cancel
</div>
</div>
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
</form>
</div>
</div>
</div>
</div>
</section>
multer and cloudinary start
var multer = require('multer');
var storage = multer.diskStorage({
filename: function(req, file, callback) {
callback(null, Date.now() + file.originalname);
}
});
var imageFilter = function (req, file, cb) {
// accept image files only
if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/i)) {
return cb(new Error('Only image files are allowed!'), false);
}
cb(null, true);
};
var upload = multer({ storage: storage, fileFilter: imageFilter})
var cloudinary = require('cloudinary');
cloudinary.config({
cloud_name: 'pitscaraccessories',
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET
});
update route
router.put("/:id", middleware.isLoggedIn, middleware.isAdmin, upload.single('image'), function(req, res) {
Product.findById(req.params.id, async function(err, prod) {
if (err) {
req.flash("error", err.message);
} else {
if (req.file) {
try {
await cloudinary.v2.uploader.destroy(prod.imageId);
var result = await cloudinary.v2.uploader.upload(req.file.path);
prod.image = result.secure_url;
prod.imageId = result.public_id;
} catch (err) {
req.flash("error", err.message);
return res.redirect("back");
}
}
prod.save();
req.flash("success", "Updated Successfully");
res.redirect("/admin");
}
});
});
fixed this by doing this to my code. changed my route from .put to .post
and manually just updating each input inside.
router.post("/:id", middleware.isLoggedIn, middleware.isAdmin, upload.single('image'), function(req, res) {
Product.findById(req.params.id, async function(err, prod) {
if (err) {
req.flash("error", err.message);
} else {
if (req.file) {
try {
await cloudinary.v2.uploader.destroy(prod.imageId);
var result = await cloudinary.v2.uploader.upload(req.file.path);
prod.image = result.secure_url;
prod.imageId = result.public_id;
} catch (err) {
req.flash("error", err.message);
return res.redirect("back");
}
}
prod.name = req.body.name;
prod.price = req.body.price;
prod.quantity = req.body.quantity;
prod.brand = req.body.brand;
prod.type = req.body.type;
prod.description = req.body.description;
prod.save();
req.flash("success", "Updated Successfully");
res.redirect("/admin");
}
});
});
and doing this to my form element
<form action="/admin/<%= prod._id %>?_csrf=<%= csrfToken %>" method="POST" enctype="multipart/form-data">
I was having the same problem, maybe you didn't try enough permutations ;D
This syntax worked for me
<form action="/users/<%= user._id %>?_method=PUT&_csrf=<%= csrfToken %>" method="POST" enctype="multipart/form-data">

Uploading image with vuejs to Nodejs Server not working

Sending form data with text content to Nodejs server from vuejs front end but the file is never uploaded to the server. I had been stack for a while now. Here is my detailed front and backend code:
Front End
//Front end VueJS
Upload form component
<template>
<div class="container">
<form v-on:submit.prevent="addMember" method="post">
<div class="row">
<div class="col-sm-10 col-md-10 col-lg-10">
<div class="form-group">
<label>Title<span class="required-field">*</span>:</label>
<input type="text" class="form-control" v-model="member.title" name="title" v-validate="'required|min:10'">
<div class="help-block alert alert-danger" v-show="errors.has('title')">
{{errors.first('title')}}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-10 col-md-10 col-lg-10">
<div class="form-group">
<label>Full Name<span class="required-field">*</span>:</label>
<input type="text" class="form-control" v-model="member.fullname" name="fullname" v-validate="'required|min:10'">
<div class="help-block alert alert-danger" v-show="errors.has('fullname')">
{{errors.first('fullname')}}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-10 col-md-10 col-lg-10">
<div class="form-group">
<label>Passport Photo
<input type="file" id="file" ref="photo" v-on:change="handleFileUpload()"/>
</label>
</div>
</div>
</div>
</form>
<div class="row">
<div class="col-sm-10 col-md-10 col-lg-10">
<div class="form-group">
<button class="btn btn-info btn-lg pull-right">Save</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name:'CreateMember',
data(){
return{
member:{},
photo:'',
}
},
},
methods: {
handleFileUpload(){
this.coverimage = this.$refs.photo.files[0]
},
addMember(){
this.$validator.validateAll().then(()=>
{
let formData = new FormData();
formData.append('file', this.photo);
let rawData = this.member
rawData = JSON.stringify(rawData)
formData.append('data',rawData)
this.axios.post('/members',this.member,{
'content-type':'multipart/form-data'
}).then((resp)=>{
console.log(resp.data)
//this.$router.push({name: 'members'})
}).catch((error)=>{
console.log(error)
})
});
}
}
}
</script>
Back End (Nodejs and Express)
**routes/members.js**
var express = require('express');
var router = express.Router();
//Require Controller Modules
var controller = require('../controllers/MembersController');
var auth = require('../middleware/auth');
const path = require('path');
const multer = require('multer');
//Define file storage engine
const storage = multer.diskStorage({
destination:'public/uploads/',
filename: (req,file,cb)=>{
cb(null,file.fieldname+'-'+Date.now()+path.extname(file.originalname));
}
});
//Filter uploaded file and only upload file types of enumerated format
const fileTypeFilter = (req, file, cb)=>{
if(file.mimetype==='image/jpeg'||file.mimetype==='image/jpg'|| file.mimetype==='image/png'){
cb(null, true);
}
else{
cb(new error('Invalid Image Format'), false);
}
}
const upload = multer({storage:storage,
limits:
{
fileSize:1024*1024*1000
},
filefilter:fileTypeFilter
});
//Save new member to the database
router.post('/members', upload.single('photo'),auth,controller.createMember);
Controllers/MembersController.js
var express = require('express');
var router = express.Router();
const fs = require('fs');
const Model = require('../models/index');
const Member = Model.sequelize.import('../models/member');
//createMember Controller Method
exports.createMember = function(req, res, next) {
//Check if the cover image is attached
if(!req.file)
{
return res.status(400).json({message:'Attach Photo!'}); //The request is returning this response, meaning the file is never uploaded. And the file actually is not being uploaded
}
Member.create({
title: req.body.title,
fullname : req.body.fullname,
photo : req.body.req.file.path
}).then(member=>{
res.json(member);
}).catch((error)=>{
return res.json({status:500,error:error});
});
};
Removing file from request the rest of the data is saved into the database as expected
Model
//Models/member.js
'use strict';
module.exports = (sequelize, DataTypes) => {
var Member = sequelize.define('Member', {
fullname: DataTypes.STRING,
photo: DataTypes.STRING
}, {});
Member.associate = function(models) {
// associations can be defined here
};
return Member;
};
What may be doing wrong. The upload directory is empty. please help me out.

Insert in bulk using Sequelize with Node and Express

Looking for a way to insert activities to the database in bulk using Sequelize Model.bulkCreate.
Not sure how to bring the activities from the view to the route function in order to insert them using bulkCreate. Right now I only insert one activity.
View:
<form action="/create" method="POST">
<div class="form-group">
<label>Plan Name</label>
<input type="text" name="plan_name" placeholder="Plan Name">
</div>
<div class="form-group">
<label>Description</label>
<input type="text" name="description" placeholder="Description">
</div>
<div class="form-group">
<label>Activity</label>
<input type="text" name="activity_name" placeholder="Activity">
<label>Comment</label>
<input type="text" name="comment" placeholder="Comment">
<input type="button" id="add" class="btn btn-success" value="Add">
</div>
<div class="form-group" id="new">
</div>
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="submit" class="btn btn-success" value="Submit">
</form>
<script type="text/javascript">
$('#add').click(function() {
let activity = $('<div class="form-group"><label>Activity</label><input type="text" name="activity_name" placeholder="Activity"><label>Comment</label><input type="text" name="comment" placeholder="Comment"></div>');
$('#new').append(activity);
});
</script>
Route:
app.post('/create', (req, res) => {
const user_id = req.user.id;
const data = {
plan_name: req.body.plan_name,
description: req.body.description,
userId: user_id
};
PlanModel.Plan.create(data).then(plan => {
const activity = {
activity_name: req.body.activity_name,
comment: req.body.comment,
plans_id: plan.id
};
ActivityModel.Activity.create(activity);
res.redirect('/');
});
});
1) Make fields to have names like an array
<div class="activities">
<div class="form-group">
<label>Activity</label>
<input type="text" name="activities[0][name]" placeholder="Activity">
<label>Comment</label>
<input type="text" name="activities[0][comment]" placeholder="Comment">
</div>
<div class="form-group">
<label>Activity</label>
<input type="text" name="activities[1][name]" placeholder="Activity">
<label>Comment</label>
<input type="text" name="activities[1][comment]" placeholder="Comment">
</div>
</div>
<div class="form-group>
<input type="button" id="add" class="btn btn-success" value="Add">
</div>
2) At server-side just take that req.body.activities and generate array of objects and call bulkCreate
app.post('/create', async (req, res) => {
try {
const data = {
plan_name: req.body.plan_name,
description: req.body.description,
userId: req.user.id
};
const plan = await PlanModel.Plan.create(data);
if (req.body.activities && Array.isArray(req.body.activities)) {
const activities = req.body.activities.map(
activity => {
return {
activity_name: activity.name,
comment: activity.comment,
plans_id: plan.id
}
});
await ActivityModel.Activity.bulkCreate(activities);
}
res.redirect('/');
}
catch(error) {
res.status(500).send(error);
}
});
3) Since body parser for urlencoded mode has extended option that parses deeply all of the fields. You've to enable it.
MAKE SURE that bodyParser middleware attached with extended: true :
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: true}));

Resources