Sorry for this simple question. I'm just new to node.js and try to make a RESTapi with express. For file upload I'm using multer from npm. Everything is work fine. But when I try to access multer property in post method by using req.file.path it's throw this message:
{
"error": {
"message": "Cannot read property 'path' of undefined"
}
}
I also try to get other property available in multer like 'mimetype' but same error just property name changed. I'm using postman to send the data.
Here is my code snippets.
product.js
import express from 'express';
import mongoose from 'mongoose';
import Product from '../models/product.model';
import multer from 'multer';
const router = express.Router();
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './uploads');
},
filename: (req, file, cb) => {
cb(null, `${new Date().toISOString().replace(/:/g, '-')}${file.originalname}`);
}
});
const fileFilter = (req, file, cb) => {
// reject a file
if(file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, false);
} else {
cb(new Error('Only .jpeg or .png files are accepted'), true);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
});
router.post('/', upload.single('productImage'), (req, res, next) => {
const product = new Product({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
price: req.body.price,
productImage: req.file.path
});
product.save().then(result => {
console.log(result);
res.status(201).json({
message: 'Created product successfully',
createdProduct: {
name: result.name,
price: result.price,
_id: result._id,
request: {
type: 'GET',
url: `http://localhost:3000/products/${result._id}`
}
}
});
}).catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
product.model.ts
import mongoose from 'mongoose';
const productSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true},
price: {type: Number, required: true},
productImage: { type: String, required: true }
});
module.exports = mongoose.model('Product', productSchema);
I can't find the solution. I've check a couple of solution from stackoverflow but didn't find any solution.
if you use postman make sure you select "Content-Type" "multipart/form-data" in the headers and in the body make sure you select form-data and the key is "productImage" and type is file then value is the file you want upload
Sending file using cURL file name "image"
curl -X PUT http://www.example.com -F "image=#/file-path"
On server end get file name
upload(req, res, (err)=> {
if (err) {
console(err);
}
let filePath = req.files.image.file; // image is filename in cURL request
});
Related
For error handling multer suggest
const multer = require('multer')
const upload = multer().single('avatar')
app.post('/profile', function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
} else if (err) {
// An unknown error occurred when uploading.
}
// Everything went fine.
})
})
I wrote a custom middleware for uploading different types of file.
const { check } = require("express-validator")
const multer = require('multer')
const mime = require('mime-types')
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
const filename = Date.now()+"-"+file.originalname
cb(null, filename)
}
})
const upload = multer({ storage: storage })
const uploadFile = (fieldname,filetypes,fileSize)=>{
return (req,res,next)=>{
let file = upload.single(fieldname)
file(req,res,function(err){
if (err instanceof multer.MulterError) {
req.fileError = {
param: "image",
msg: "Unable to process request"
}
return next()
} else if (filetypes.includes(mime.extension(req.file.mimetype)) === false) {
req.fileError = {
param: "image",
msg: `Only ${filetypes.toString()} allowed`
}
return next()
} else if (req.file.size > fileSize) {
req.fileError = {
param: "image",
msg: `File size should not exceed ${formatBytes(req.file.size)}`
}
return next()
}
})
}
}
course_validator = [
check("name")
.trim()
.isLength({min:3,max:100})
.withMessage("Course name should be between 3 to 100 characters")
]
app.get("/create/post",uploadFile("image",["jpeg","jpg"],122880),(req,res)=>{
const errors = validationResult(req)
if(!errors.isEmpty()){
return res.json({
status: false,
error: req.fileError ? [...errors.array(),req.fileError] : errors.array()
})
}
})
If there is no error then only I need to upload the file to uploads folder. When I upload a other than jpeg or jpg I am getting error with message that Only jpeg,jpg allowed. This is what I need. But the problem is the file is also getting uploaded to uploads folder.
For custom error messages you can go through this controller here I'm checking to file type when uploading an image and in the controller, if there is no file selected at that time I'm sending a custom message with simple if the condition after passing all if image and the products will be saved in DB
exports.postProduct = (req, res, next) => {
const title = req.body.title;
const image = req.file;
const price = req.body.price;
const description = req.body.description;
if (!image) {
return res.status(422).render("admin/add-product", {
pageTitle: "Add Product",
path: "/adminproducts",
hasError: true,
product: {
title: title,
price: price,
description: description,
},
errorMessage: "Atteched file is not an image!!!",
validationErrors: [],
});
}
const imageUrl = image.path;
const product = new Product({
title: title,
imageUrl: imageUrl,
price: price,
description: description,
userId: req.user,
});
product
.save()
.then((results) => {
console.log("Product Created Successfully");
res.redirect("/admin/products");
})
.catch((err) => {
console.log(err);
});
};
Hey please am new to Vuejs and Express...So am trying to practice.
So am trying to create a User Profile with comes with a image Using Vuejs and ExpressJs but none of the file or text is uploading.
This is my CreateProfile.vue file
<div class="icon-pic">
<label for="Password">Upload your Logo / Picture</label>
<input type="file" ref="file" #change="handleFileUpload"/>
</div>
<b-input-group class="mb-2">
<b-form-input
id="input-small"
type="text"
placeholder="Enter your Name"
required
:rules="[rules.required]"
v-model="profile.fullname"
></b-form-input>
<b-form-input
id="input-small"
type="text"
placeholder="Enter your BrandName"
v-model="profile.brandname"
></b-form-input>
</b-input-group>
Note: There are other inputs...
Below is my script functions for the form
<script>
import ProfileService from '#/services/ProfileService'
export default {
data () {
return {
profile: {
fullname: null,
brandname: null,
skill1: null,
skill2: null,
skill3: null,
skill4: null,
socail_handle1: null,
socail_handle2: null
},
file: null,
error: null,
rules: {
required: (value) => !!value || 'Required.'
}
}},
methods: {
handleFileUpload () {
const file = this.$refs.file.files[0]
this.file = file
},
async create () {
this.error = null
const formData = new FormData()
formData.append('file', this.files)
const areAllFieldsFilledIn = Object.keys(this.profile).every(
(key) => !!this.profile[key]
)
if (!areAllFieldsFilledIn) {
this.error = 'Please fill in all the required fields.'
return
}
try {
await ProfileService.post(this.profile, formData)
this.$router.push({
name: 'profile'
})
} catch (error) {
this.error = error.response.data.error
}
}}}
Below is my ProfileController.js file
const {Profile} = require ('../models')
const multer = require ('multer')
const fileFilter = (req, file, cb) => {
const allowedTypes = ["image/jpeg", "image/jpg", "image/png"]
if (!allowedTypes.includes(file.mimetype)){
const err = new Error('Incorrect File');
return cb(err, false)
}
cb(null, true)
}
const upload = multer ({
dest: '../public',
fileFilter,
})
module.exports = {
async post (req, res){
try {
upload.single('files')
const profile = await new Profile({
profile: this.profile,
files: req.file
});
profile.save().then(result => {
console.log(result);
res.status(201).json({
message: "Done upload!"
})
})
} catch (err) {
console.log(err)
res.status(500).send({
error: 'An Error has occured trying to fetch'
})}}
Follow by my Model/Profile.js file
module.exports = (sequelize, DataTypes) => {
const Profile = sequelize.define('Profile', {
files: {
type: DataTypes.JSON
},
fullname: {
type: DataTypes.STRING,
allowNull: false
},
brandname: DataTypes.STRING,
skill1: DataTypes.STRING,
skill2: DataTypes.STRING,
skill3: DataTypes.STRING,
skill4: DataTypes.STRING,
socail_handle1: DataTypes.STRING,
socail_handle2: DataTypes.STRING
})
return Profile
}
I hope any one can help me with this please!!!
This is my route.js file
const AuthController = require('./controllers/AuthController')
const AuthControllerPolicy = require('./policies/AuthControllerPolicy')
const ProfileControler = require('./controllers/ProfileController')
const upload = require ('multer')
module.exports = (app) => {
app.post('/register',
AuthControllerPolicy.register,
AuthController.register)
app.post('/login',
AuthController.login)
app.get('/profile',
ProfileControler.index)
app.post('/upload', upload.single('file'),
ProfileControler.upload)
}
I notice two things:
You're not using multer as a middleware function
upload.single('file') returns a function which should be passed as a middleware in your Express routes. You can use it like this in your route.js:
const multer = require('multer');
const upload = multer({
dest: '../public',
fileFilter,
});
app.post('/upload', upload.single('file'), ProfileController.post);
Then you can remove the upload code in your post function:
module.exports.post = async (req, res) => {
// Multer makes your file available at req.file
const file = req.file;
try {
// Don't need to await when creating a new Mongo object
const profile = new Profile({
profile: this.profile,
files: file
});
// Refactored this to use async/await instead of promises.
// Avoid mixing promises with async/await.
const result = await profile.save();
return res.status(201).json({ message: "Done upload!" });
} catch (error) {
console.log(error)
return res.status(500).send({ error: 'An Error has occured trying to fetch' });
}
}
The name of the file input passed to multer doesn't match with frontend
You're configuring multer to look for a file input named files: upload.single('files'), yet in the frontend you're naming it file (singular): formData.append('file', this.files). Usually multer will then throw an unexpected field error. Make sure these two match exactly.
This free guide for Parsing Requests in Node.js will help you handle file uploads in Node.js.
I'm trying to create a rest api that would accept some simple data for an item. I used to use multer as file handler in my project. Here is my code for importing and handling the multer config:
const multer = require('multer')
const upload = multer({ dest: 'uploads/' })
And here is the corresponding app.post route for that:
// POST
router.post('/', upload.single('pi') ,(req, res, next) => {
console.log(req.body)
// console.log(req.file);
console.log('hi there')
const product = new Product({
// _id: new mongoose.Types.ObjectId(),
nm: req.body.name,
pr: req.body.price,
ca: req.body.category,
ti: req.body.title,
tg: req.body.tag
})
product
.save()
.then(result => {
res.status(201).json({
message: 'Handeling POST requests...',
createdProduct: {
_id: result._id,
nm: result.nm,
pr: result.pr,
ca: result.ca,
ti: result.ti,
tg: result.tg,
meta: {
type: 'get',
url: 'http://localhost:4000/products/' + result._id,
}
}
})
// console.log(result)
})
.catch(err => {
console.log(err)
res.status(500).json({
error: err
})
})
})
The Problem
The problem is that file is being uploaded to the uploads/ directory but the product is not being created. After the file is being uploaded to the corresponding directory I'm getting Could not get any response in Postman. Also I tried to log the req.body inside the multer but I did not get any thing in my terminal. I also set my Content-Type: to application/x-www-form-urlencoded and I'm using form-data in postman.
I setup multer like this
let multer = require('multer');
let apiRoutes = express.Router();
let UPLOAD_PATH = '../uploads';
let storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, UPLOAD_PATH);
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now());
}
});
let upload = multer({ storage: storage });
and in route I am getting data and an image and use multer like!
apiRoutes.post('/update', passport.authenticate('jwt', { session: false }), (request, response) => {
let record = {
name: request.body.name,
location: request.body.location,
about: request.body.about,
userid: request.body.userid,
avatar: request.body.filename
};
let userData = {
name: request.body.name
};
if (request.body.filename) {
upload(request, response, (error) => {
});
}
profile.findOneAndUpdate({ userid: request.body.userid }, record, {new: true}, (error, doc) => {
if (error) response.json(error);
user.findOneAndUpdate({ _id: request.body.userid }, record, (error, result) => {
if (error) throw error;
response.json(doc);
});
});
});
What is happening with this code is that when I do not send an image to backend then I get data from front end and store it into database. But when I send image along side data then it return POST /api/1.0/profile/update 401 0.396 ms - -.
It means I am not getting any data at all. Whats wring with the code here?
You can't use Multer in your /update route. Use Multer in your router like this:
var upload = multer({ dest: 'uploads/' })
apiRoutes.post('/profile', upload.single('image'), function (req, res, next) {
// Uploaaded
})
if you add it and still can't get our file, you should update your form with this parameter: enctype="multipart/form-data"
I'm new to node.js and try to built a simple REST Api with file uploading capabilities. For file upload I'm using multer from npm and using POSTMAN for sending post request. But when I try to upload file with multer I got this error:
{
"error": {
"message": "Cannot read property 'path' of undefined"
}
}
Here is my code for the API
product.js
import express from 'express';
import mongoose from 'mongoose';
import Product from '../models/product.model';
import multer from 'multer';
import multipart from 'connect-multiparty';
const router = express.Router();
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './uploads');
},
filename: (req, file, cb) => {
cb(null, `${new Date().toISOString().replace(/:/g, '-')}${file.originalname}`);
}
});
const fileFilter = (req, file, cb) => {
// reject a file
if(file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, false);
} else {
cb(new Error('Only .jpeg or .png files are accepted'), true);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
});
router.post('/', upload.single('productImage'), (req, res, next) => {
const product = new Product({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
price: req.body.price,
productImage: req.file.path
});
product.save().then(result => {
console.log(result);
res.status(201).json({
message: 'Created product successfully',
createdProduct: {
name: result.name,
price: result.price,
_id: result._id,
request: {
type: 'GET',
url: `http://localhost:3000/products/${result._id}`
}
}
});
}).catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
And here is the model
product.model.js
import mongoose from 'mongoose';
const productSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true},
price: {type: Number, required: true},
productImage: { type: String, required: false }
});
module.exports = mongoose.model('Product', productSchema);
Even I use connect-multipart middleware to fix the issue but then I got other error:
{
"error": {
"message": "stream ended unexpectedly"
}
}
Here is the code when use multipart
router.post('/', upload.single('productImage'), multipart(), (req, res, next) => {
const product = new Product({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
price: req.body.price,
productImage: req.file.path
});
product.save().then(result => {
console.log(result);
res.status(201).json({
message: 'Created product successfully',
createdProduct: {
name: result.name,
price: result.price,
_id: result._id,
request: {
type: 'GET',
url: `http://localhost:3000/products/${result._id}`
}
}
});
}).catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
I don't think it's a problem about POSTMAN.
Now can anyone help me to fix this. It's really a big problem for me to go ahead without solving the issue....
Thanks in advance.
I think the issue is that your call back is reversed. If the file is jpeg or png you will want (null,true). See below:
const fileFilter = (req, file, cb) => {
// reject a file
if(file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true);
} else {
cb(new Error('Only .jpeg or .png files are accepted'), false);
}
};