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.
Related
I want to create an endpoint which should store file on server side as well as the some json data which should be received from same point is to be stored on mongodb.
I am using axios to send request from React App. Here is my Code.
const [companyFile, setCompanyFile] = useState(null);
const [company, setCompany] = useState({
name: "",
websiteUrl: "",
email: "",
companyLocation: "",
});
const AddCompany = async (e) => {
if (companyFile) {
e.preventDefault();
let formData = new FormData();
formData.append("company-file", companyFile);
formData.append("company", JSON.stringify(company));
axios({
method: "post",
url: `http://localhost:8080/company/add`,
data: formData,
withCredentials: true,
header: {
Accept: "application/json",
"Content-Type": "multipart/form-data",
},
}).then((res) => console.log(res.data));
} else {
console.log("file not selected!!!!");
}
};
Now I don't know how to check if it is coming to backend express server or not? Or if coming then how to retrive application/json data from request for further process.
My question is that if data is sent to backend express then how to process that data in backend (i.e. get json data to create an document in mongodb).
Here is my code for company/add
let upload = multer({
storage: multer.diskStorage({
destination: async (req, file, cb) => {
if (!company) {
throw Error("Company cannot be found!");
}
let companyName = req.company.name;
let path = `./uploads/${companyName}/files`;
if (!fs.existsSync(path)) {
fs.mkdirSync(path, { recursive: true });
}
cb(null, path);
},
filename: async (req, file, cb) => {
// ** with student auth Code
req.filename = req.company.name;
cb(null, filename + path.extname(file.originalname));
},
}),
}).single("company-file");
// Add Company
module.exports.add_company = async (req, res) => {
try {
// Here I want to extract that company object to create new company
console.log(req.file);
try {
const newcompany = await Company.create(company);
req.company = newcompany;
upload(req, res, async () => {
try {
const companyFile = await CompanyFile.create({
companyId: req.company._id,
path: `./uploads/${req.company.name}/file/${req.filename}.pdf`,
});
req.companyFile = companyFile;
} catch (err) {
res.status(400).json({ success: false, message: err.message });
// ** code for resume-upload using student authentication middleware
if (
fs.existsSync(
`./uploads/${req.company.name}/file/${req.filename}.pdf`
)
) {
fs.unlink(`./uploads/${req.company.name}/file/${req.filename}.pdf`);
}
}
});
res.status(201).json({
success: true,
message: "Company Drive Added Successfully.",
company: req.company,
companyFile: req.companyFile,
});
} catch (err) {
res.status(400).json({
success: false,
errors: err,
message: "Error while applying company drive.",
});
}
} catch (err) {
console.log(err);
}
};
From the docs:
Multer adds a body object and a file or files object to the request
object. The body object contains the values of the text fields of the
form, the file or files object contains the files uploaded via the
form.
So in your case, you can access and parse the company field by doing:
const company = JSON.parse(req.body.company);
Also, you need to make sure to apply the multer middleware to your controller, e.g.
app.post('/company/add', upload.single('company-file'), function (req, res, next) {
// req.file is the `company-file` file
// req.body.company will hold the company text field
})
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);
});
};
I'm trying to set up multer but I'm having some issues with the image, as for now, when I don't upload any image, the server gives me an error which doesn't allow me to continue. I want that even if the image is not uploaded, to still store the values in the database.
This is where I have my multer configured:
const express = require('express');
const router = express.Router();
const multer = require('multer');
const Clients = require('../models/clients.js')
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, "../emaildragger/public/uploads")
},
filename: (req, file, callback) => {
callback(null, file.originalname)
}
})
const upload = multer({storage: storage})
router.route('/').get((req, res) => {
Clients.find()
.then(client => res.json(client))
.catch(err => res.status(400).json('Error:' + err))
})
router.route('/:id').get((req, res) => {
Clients.findById(req.params.id)
.then(client => res.json(client))
.catch(err => res.status(400).json("Error" + err))
})
router.route('/add').post(upload.single("image"), (req, res) => {
const newClient = new Clients({
image: req.file.originalname,
firstName: req.body.firstName,
lastName: req.body.lastName,
weight: req.body.weight,
BMI: req.body.BMI
})
newClient.save()
.then (() => res.json(newClient))
.catch(err => res.status(400).json('error' + err))
})
module.exports = router
Here is my models:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var clientsSchema = new Schema(
{
image:
{ type: String, required: false, default: 'Screenshot_293.png'},
firstName: { type: String, required: true },
lastName: { type: String, required: true },
weight: { type: String, required: false },
BMI: { type: String, required: false }
}
);
const Clients = mongoose.model("clients", clientsSchema)
module.exports = Clients
The error is in your server because you added Multer as an middleware, and later in your controller you are trying to access the originalname of the uploaded file. If you didn't send the image, then Multer will not parse and upload it, and the file will not exist. In that case you will try to access to the originalname property of something that does not exist, so your server will throw an error.
Try to change you code like this:
router.route('/add').post(upload.single("image"), (req, res) => {
let client = {
firstName: req.body.firstName,
lastName: req.body.lastName,
weight: req.body.weight,
BMI: req.body.BMI
}
if(req.file && req.file.originalname) client.image = req.file.originalname;
const newClient = new Clients(client)
newClient.save()
.then (() => res.json(newClient))
.catch(err => res.status(400).json('error' + err))
})
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
});
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);
}
};