Multer is uploading my image locally but not in my database - node.js

I Want to upload a image on my blog post and there is an error, image is alredy in the folder but when i see the database there is noimage.png set as default ...
Im using Multer , there is my code :
Addpost.ejs
<% include ./partials/header %>
<form method="POST" action="/posts/add" enctype="multipart/form-data">
<div class="form-group">
<label>Title</label>
<input class="form-control" name="title" type="text">
</div>
<div class="form-group">
<label>Category</label>
<select class="form-control" name="category">
<% categories.forEach(function(category){ %>
<option value="<%= category.title %>"><%= category.title %></option>
<% }) %>
</select>
</div>
<div class="form-group">
<label>Body</label>
<textarea class="form-control" name="body"></textarea>
</div>
<div class="form-group">
<label>Main Image</label>
<input class="form-control" name="file" type="file">
</div>
<div class="form-group">
<label>Author</label>
<select class="form-control" name="author">
<option value="Arsen Cenko">Arsen Cenko</option>
<option value="John Doe">John Doe</option>
</select>
</div>
<input class="btn btn-default" name
="submit" type="submit" value="save">
</form>
<script src="/app.js"></script>
PostSchema :
var mongoose = require("mongoose")
var postSchema = new mongoose.Schema({
title: String,
created: {type: Date, default: Date.now},
category: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Category"
}
],
body: String,
file: String })
module.exports = mongoose.model("Post", postSchema);
Post.js Route
var express = require("express");
var router = express.Router();
var Post = require("../models/post");
var Category = require("../models/category");
var path = require('path');
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/images/uploads/')
},
filename: function (req, file, cb) {
cb(null, Date.now() + path.extname(file.originalname))
} });
var upload = multer({ storage: storage });
router.get("/add", function(req, res) {
Category.find({}, function(err, categories){
if(err){
console.log(err);
} else{
res.render("addpost", {
title: "Add Post",
categories: categories
})
}
})
})
router.post("/add", upload.single('image'), function(req, res){
var title = req.body.title;
var category = req.body.category;
var body = req.body.body;
var author = req.body.author;
if(req.files && req.files.image){
var imageOriginalName = req.files.image.originalname;
var imageName = req.files.image.fieldname;
var imageMime = req.files.image.mimetype;
var imagePath = req.files.image.path;
var imageExt = req.files.image.extension;
var imageSize = req.files.image.size;
} else {
var imageName = "noimage.png";
}
var newPost = {title:title, category:category, body:body, author:author, image:imageName};
Post.create(newPost, function(err, newPost){
if(err){
console.log("Error");
} else{
res.redirect("/");
}
})
})
Thanks :)

Hey according to documentation
https://github.com/expressjs/multer
using upload.single('image') will get your file in "req.file" not in "req.files"
I think this is the reason your if case fails and jumps to else case. You cannot access it like req.file.image.filename you can simply access it via req.file.filename. Since it is already a "single" file. try console logging the req.file to know what you are getting.
update I think mistake is in your html
<input class="form-control" name="file" type="file">
should be
<input class="form-control" name="image" type="file">
the name attribute of the html should be same as the string in upload.single.
So in your case
upload.single('image')
Now you will receive file in req.file.
Hope it helps.

Related

File Fail To Upload

Started learning nodejs recently, and am currently trying to perform a task that involves file upload.. With the aid of multer documentation and Youtube video, was able to set up the module and middleware and file path etc. but for some reasons, the file don't upload. i can see the file object in my console, but my browser keeps loading.
Would love if someone can help point my errors to allow the file upload to specifies folder, and also give me a tip on how to trap errors not rendered on the error file Thank you. Below are my codes:
register.hbs
{{>header}}
<body>
<h1>Hello World</h1>
<h2>{{this.title}}</h2>
<div class="mb-3">
{{#each data}}
<div class="alert alert-danger">{{msg}}</div>
{{/each}}
</div>
<form action="" method="POST" enctype="multipart/form-data">
<label for="exampleInputEmail1" class="form-label">First Name</label>
<input type="text" class="form-control" name="fname" aria-describedby="emailHelp">
<div>
</div>
</div>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Last Name</label>
<input type="text" class="form-control" name="lname" aria-describedby="emailHelp">
</div>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Email address</label>
<input type="email" class="form-control" name="email" aria-describedby="emailHelp">
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Password</label>
<input type="password" class="form-control" name="pwd">
</div>
<div class="mb-3">
<label for="exampleInputFile" class="form-label">Select Image</label>
<input type="file" class="form-control" name="image" id="image">
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">Check me out</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</body>
</html>
router file
var express = require('express');
var router = express.Router();
const {check,validationResult}= require('express-validator');
const multer=require('multer')
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/uploads')
},
filename: function (req, file, cb) {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)
cb(null, file.fieldname + '-' + uniqueSuffix)
}
})
var upload = multer({
storage: storage,
limits:{
fieldSize:1024*1024*100
} })
router.post('/register',upload.single('image'), [
check('fname')
.isLength({ min: 8 })
.withMessage('First name must be minimum of 8 characters')
.isAlpha()
.withMessage('first name must contain only alphabeth'),
check('lname').isLength({min:8}),
check('email').isEmail()
], (req, res,next)=>{
const errors = validationResult(req)
if (!errors.isEmpty()) {
//const data=errors.array();
console.log(errors)
res.render('users/register',{title:"Create Account", data:errors.array()});
//return res.status(422).json({errors: errors.array()})
}
console.log(req.file)
const fname = req.body.fname
const lname = req.body.lname
const email = req.body.email
console.log(req.errors)
// res.render('users/register');
})
Not really sure about your case, in my case I using multer like this:
I seperate multer to a file multerUploaderMiddleware.js
const multer = require("multer");
const path = require("path");
const util = require("util");
//Change this to diskStorage like your code above
//I use memory storage
const multerStorage = multer.memoryStorage()
function chapterIsExisted(req) {
// My custom error handling function
return (
Chapter
.findOne({ comicSlug: req.params.slug, chapter: `chapter-${req.body.chapter}` })
.then(chapterExisted => {
if (chapterExisted == null) { return false }
return true
}))
};
// This filter function for error handling
const filter = async (req, file, cb) => {
//as I remember if you want the file to success
// then return cb(null, true)
// else return cb(null, false)
if (req.check == 'nocheck') {
return cb(null, true)
} else {
var check = await chapterIsExisted(req)
if (check == false) {
cb(null, true)
} else {
let errorMess = `chapter ${req.body.chapter} existed`
return cb(errorMess, false);
}
}
};
const upload = multer({
storage: multerStorage,
fileFilter: filter
}).single("image", 200);
// Turn this middleware function into promise
let multipleUploadMiddleware = util.promisify(upload);
module.exports = multipleUploadMiddleware;
And In my route controller:
// get the exported function
const MulterUploadMiddleware =
require("../middlewares/MulterUploadMiddleWare");
class S3UploadController {
// [POST] / stored /comics /:slug /S3-multiple-upload
multipleUpload = async (req, res, next) => {
// The function you exported from middleware above
MulterUploadMiddleware(req, res)
.then(async () => {
//when the upload is done you get some data back here
Console.log(req.file)
// do stuff
})
.then(() => res.redirect('back'))
.catch(err => next(err))
})
}

Node.js, Multer. video file is saved/uploaded but validator gives an error that video field can not remain empty

I'm trying to upload a video using multer, I have a form and everything but when I click create, validator gives an error however, the file is saved in my directory as if validation was successful.
I'm using mongoDB for database.
here's the validator
const { check } = require('express-validator/check');
const Video = require('app/models/VideoModel');
const path = require('path');
class videoValidator extends validator {
handle() {
return [
check('title')
.isLength({ min : 5 })
.withMessage('title should be at least 5 characters'),
check('artist')
.not().isEmpty()
.withMessage('artist field can not be empty'),
check('videos')
.custom(async (value , { req }) => {
if(req.query._method === 'put' && value === undefined) return;
if(! value)
throw new Error('the video field can not remain empty');
let fileExt = ['.webm' , '.ogg' , '.mp4' , '.avi'];
if(! fileExt.includes(path.extname(value)))
throw new Error('file extention is not supported')
}),
}
slug(title) {
return title.replace(/([^۰-۹آ-یa-z0-9]|-)+/g , "-")
}
}
module.exports = new videoValidator();
video Controller
async create(req , res) {
let artists = await Artist.find({});
res.render('admin/videos/create' , { artists });
}
async store(req , res , next) {
try {
let status = await this.validationData(req);
if(! status) return this.back(req,res);
let videos = req.file;
let { title , artist } = req.body;
let newVideo = new Video({
artist,
title,
slug : this.slug(title),
videos,
});
await newVideo.save();
return res.redirect('/admin/videos');
} catch(err) {
next(err);
}
}
Routes and upload helper
const videoUpload = require('app/helpers/uploadVideo');
// video Routes
router.get('/videos' , videoController.index);
router.get('/videos/create' , videoController.create);
router.post('/videos/create' ,
videoUpload.single('videos'),
videoValidator.handle(),
videoController.store
);
const multer = require('multer');
const path = require('path');
const mkdirp = require('mkdirp');
const fs = require('fs');
const getDirVideo = () => {
let year = new Date().getFullYear();
let month = new Date().getMonth() + 1;
let day = new Date().getDay();
return `./public/uploads/videos/${year}/${month}/${day}`;
}
const videoStorage = multer.diskStorage({
destination : (req , file , cb) => {
let dir = getDirVideo();
mkdirp(dir , (err) => cb(null , dir))
},
filename : (req , file , cb) => {
let filePath = getDirVideo() + '/' + file.originalname;
if(!fs.existsSync(filePath))
cb(null , file.originalname);
else
cb(null , Date.now() + '-' + file.originalname);
}
})
const uploadVideo = multer({
storage : videoStorage,
limits : {
fileSize : 1024 * 1024 * 40
}
});
module.exports = uploadVideo;
Model and ejs
const videoSchema = Schema({
artist: {
type : Schema.Types.ObjectId,
ref : 'Artist'
},
title: {
type : String,
required : true
},
videos: {
type : Object,
required : true
},
time: {
type: String,
default : '00:00:00'
},
videoUrl: {
type : String,
required : true
},
} , { timestamps : true });
module.exports = mongoose.model('Video' , videoSchema);
<div class="d-flex justify-content-between align-items-center mb-3 pb-2 border-bottom">
<h2>add Video</h2>
</div>
<form class="form-horizontal" action="/admin/videos/create" method="post" enctype="multipart/form-data">
<%- include(viewPath('layouts/error-messages')) -%>
<div class="form-group row">
<div class="col">
<label for="title" class="control-label font-weight-bold">Video Title </label>
<input type="text" class="form-control" name="title" id="title" placeholder="video title" value="<%= old('title') %>">
</div>
</div>
<div class="form-group row">
<div class="col">
<label for="artist" class="control-label font-weight-bold font-weight-bold">linked Artist</label>
<select name="artist" id="artist" class="form-control">
<% artists.forEach(artist => { %>
<option value="<%= artist._id %>" <%= String(old('artist')) == String(artist._id) ? 'selected' : '' %> ><%= artist.name %></option>
<% }) %>
</select>
</div>
<div class="col">
<label for="time" class="control-label font-weight-bold">Video Length</label>
<input type="text" class="form-control" name="time" id="time" placeholder="enter video length" value="<%= old('time') %>">
</div>
</div>
<div class="form-group row">
<div class="col">
<label for="videos" class="control-label font-weight-bold">Video File</label>
<input type="file" class="form-control" name="videos" id="videos" placeholder="Enter video file" >
</div>
</div>
<div class="form-group row">
<div class="col">
<button type="submit" class="btn btn-danger">Create</button>
</div>
</div>
</form>
and here's a screen shot of my upload folder that has the video correctly saved in it and it's playable
enter image description here
I just realized I've forgotten to add convertFileToField middleware.
it's working fine now
handle(req , res , next) {
if(! req.file)
req.body.videos = undefined;
else
req.body.videos = req.file.filename;
next();
}

Express.js application bug: Cannot read property 'transfer-encoding' of undefined

I am working on a blogging application (click the link to see the GitHub repo) with Express, EJS and MongoDB.
I am trying to introduce an add post image feature. Being quite new to Express, I am puzzled about the problem I have ran into.
The add post form:
<form action="/dashboard/post/add" method="POST" enctype="multipart/form-data" class="mb-0">
<div class="form-group">
<input type="text" class="form-control" name="title" value="<%= typeof form!='undefined' ? form.titleholder : '' %>" placeholder="Title" />
</div>
<div class="form-group">
<input type="text" class="form-control" name="excerpt" value="<%= typeof form!='undefined' ? form.excerptholder : '' %>" placeholder="Excerpt" />
</div>
<div class="form-group">
<textarea rows="5" class="form-control" name="body" placeholder="Full text"><%= typeof form!='undefined' ? form.bodyholder : '' %></textarea>
</div>
<label for="postimage">Upload an image</label>
<div class="form-group">
<input type="file" name="postimage" id="postimage" size="20">
</div>
<div class="form-group d-flex mb-0">
<div class="w-50 pr-1">
<input type="submit" value="Add Post" class="btn btn-block btn-md btn-success">
</div>
<div class="w-50 pl-1">
Cancel
</div>
</div>
</form>
In the controller my addPost() methos looks like this:
const Post = require('../../models/post');
const { validationResult } = require('express-validator');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads/images')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() + '.png')
}
});
const upload = multer({ storage: storage }).single('postimage');
exports.addPost = (req, res, next) => {
upload(function(err) {
if (err) {
console.log("There was an error uploading the image.");
}
res.json({
success: true,
message: 'Image uploaded!'
});
})
var form = {
titleholder: req.body.title,
excerptholder: req.body.excerpt,
bodyholder: req.body.body
};
const errors = validationResult(req);
const post = new Post();
post.title = req.body.title;
post.short_description = req.body.excerpt;
post.full_text = req.body.body;
if (!errors.isEmpty()) {
req.flash('danger', errors.array())
res.render('admin/addpost', {
layout: 'admin/layout',
website_name: 'MEAN Blog',
page_heading: 'Dashboard',
page_subheading: 'Add New Post',
form: form
});
} else {
post.save(function(err) {
if (err) {
console.log(err);
return;
} else {
req.flash('success', "The post was successfully added");
req.session.save(() => res.redirect('/dashboard'));
}
});
}
}
I alse have const multer = require("multer"); at the top (of the controller).
The "Add New Post" form worked fine until I tried to add this upload feature. The code I currently have throws this error:
Cannot read property 'transfer-encoding' of undefined
at hasbody (C:\Path\To\Application\node_modules\type-is\index.js:93:21)
What am I doing wrong?
You are missing req and res inside your upload(), try adding those two like
upload(req, res, function (err) {
if (err) {
console.log("There was an error uploading the image.");
}
res.json({
success: true,
message: 'Image uploaded!'
});
})

Working form with file upload image in expressjs

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

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.

Resources