I added multer to my node js app and it works fine, just the path of the image which I need to store in db isn't correct. Can't find the problem, its obviously some stupid mistake made by me.
This is my setup for multer
const multer = require('multer');
const storage = multer.diskStorage({
destination: './public/images',
filename: function(req, file, next){
next(null, Date.now() + '-' + file.originalname);
}
});
const upload = multer({ storage: storage});
Here is how I use it to store the path
router.post('/add', upload.single('myImage'), function(req, res){
req.checkBody('title','Title is required').notEmpty();
//req.checkBody('author','Author is required').notEmpty();
req.checkBody('body','Body is required').notEmpty();
// Get Errors
let errors = req.validationErrors();
if(errors){
res.render('add_article', {
title:'Add Article',
errors:errors
});
} else {
let article = new Article();
//var date = new Date();
article.title = req.body.title;
article.author = req.user._id;
article.body = req.body.body;
article.descript = req.body.descript;
article.category = req.body.category;
article.date = getDate();
article.time = getTime();
article.comments = 0;
article.img = req.file.path;
console.log(req.file);
article.save(function(err){
if(err){
console.log(err);
return;
} else {
req.flash('success','Article Added');
res.redirect('/');
}
});
}
});
You can see from here that the path isnt right and I can't use it on GET
{ _id: 5bd993756373a5182460aa2a,
title: 'Sport 5',
author: '5acab056708e0d1248cba6ed',
body: 'sadddddddddddddd213',
descript: 'dsadas',
category: 'sport',
date: '2018/10/31',
time: '12:35',
comments: 0,
img: 'public\\images\\1540985717747-nike_logo_slogan_sport_advertising_42643_1280x1024.jpg',
__v: 0 }
Multer is working correctly on your end you just need to convert the system path into a url accessible one.
Here this will help you.
article.comments = 0;
let fileUrl = req.file.path.replace(/\\/g, "/").substring("public".length);
article.img = fileUrl;
This happens in windows because any file path in windows has back slash only but to make the file accessible through url it has to have front slash. So just use the below code to convert all backslash to front slash
const imageUrl = req.file.path.replace("\", "/");
Related
What Changes do I need to make in order to proceed with this task. There isn't much documentation on how to upload multiple files to google cloud. I have some experience with nodejs please contribute. I'm able to do it for single upload but not for multiple upload. This code basically should do what says in the question
Here's my route
router.post('/:pid/images',
fileUpload.multer.array('images', 6),
fileUpload.sendUploadToGCS,
postsControllers.addImagestoPost);
Here's my fileUpload.js
const Multer = require('multer');
const { uuid } = require('uuidv4');
const {Storage} = require('#google-cloud/storage');
const path = require('path');
const MIME_TYPE_MAP = {
'image/png': 'png',
'image/jpeg': 'jpeg',
'image/jpg': 'jpg',
};
const gc = new Storage({
keyFilename: path.join(__dirname,'../########.json'),
projectId: '#####'
});
const craigFilesBucket = gc.bucket('craig-files');
function getPublicUrl(filename) {
return `https://storage.googleapis.com/craig-files/${filename}`;
}
function sendUploadToGCS(req, res, next){
if(!req.file){
return next();
}
const gcsname = uuid() + req.file.originalname;
const file = craigFilesBucket.file(gcsname);
const stream = file.createWriteStream({
metadata: {
contentType: req.file.mimetype,
},
resumable: false
});
stream.on('error', err =>{
req.file.cloudStorageError = err;
next(err);
});
stream.on('finish', async () => {
req.file.cloudStorageObject = gcsname;
await file.makePublic();
req.file.cloudStoragePublicUrl = getPublicUrl(gcsname);
next();
});
stream.end(req.file.buffer);
}
const multer = Multer({
storage: Multer.MemoryStorage,
limits: {
fileSize: 10 * 1024 * 1024, // no longer than 10mb
}
});
module.exports = {
getPublicUrl,
sendUploadToGCS,
multer
};
Here is my controller
const addImagestoPost = async (req, res, next) =>{
const errors = validationResult(req);
if(!errors.isEmpty()){
return next(new HttpError('Invalid inputs passed, please check your data.', 422))
}
const postId = req.params.pid;
let post;
try{
post = await Post.findById(postId);
}catch(err){
const error = new HttpError(
'Something went wrong could not update post',
500
);
return next(error);
}
if(!req.file || !req.file.cloudStoragePublicUrl){
return next(new HttpError('cloudStoragePublicUrl err.', 422))
}
post.images.push(req.file.cloudStoragePublicUrl);
try{
await post.save();
}catch(err){
const error = new HttpError(
'Something went wrong, could not update post', 500
)
}
res.status(200).json({post: post.toObject({getters: true})})
}
The API itself does not contain multiply upload of the files. So if you are able to upload one file I suggest to implement some method that will loop over your files.
You may take a look as well on "convenience method" Bucket.upload which I think is very nice equivalent of File.createWriteStream.
I am uploading a text file using multer. I need to get the content of the text file. So, I am using buffer.toString('utf8'). But, it is giving me an error message which is as follows: Cannot read property 'toString' of undefined.Does anyone have any idea that why is it happening? Also, I have checked that req.file is not undefined
My code is as follows:
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
function setupRoutes(app) {
//#TODO add appropriate routes
const base = app.locals.base;
// app.get(`${base}/search.html`, doSearch(app));
app.get(`${base}/add.html`, createDocsForm(app));
app.post(`${base}/add.html`, upload.single('file'), doAdd(app));
app.get(`${base}/:id`, showContent(app));// must be last
}
function doAdd(app){
return async function(req, res) {
try{
//console.log(req.file.buffer.toString(`utf8`));
// if(req.file === undefined) console.log("req is undefined");
const fileContent = req.file.buffer.toString('utf8');
// const fileContent = req.file;
let fileName = req.file.originalname;
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
const obj = {
name: fileName,
content: fileContent
}
const a = await app.locals.model.uploadDoc(obj);
res.redirect(`${app.locals.base}/${fileName}`);
}catch(err){
if(req.file == undefined){
const err = "Select file to upload";
const model = {base:app.locals.base, err:err};
const html = doMustache(app,'add',model);
res.send(html);
}
else{
const errors= wsErrors(err);
const model = {base:app.locals.base, errors:errors};
const html = doMustache(app,'add',model);
res.send(html);
}
} }
}
You have conflicting code:
const upload = multer({ dest: 'uploads/' });
If you want file.buffer, you'll need to import without dest like:
const upload = multer({});
Your import implies DiskStorage, but multer only defines buffer on files using MemoryStorage:
function Multer (options) {
if (options.storage) {
this.storage = options.storage
} else if (options.dest) {
this.storage = diskStorage({ destination: options.dest })
} else {
this.storage = memoryStorage()
}
this.limits = options.limits
this.preservePath = options.preservePath
this.fileFilter = options.fileFilter || allowAll
}
See multer's storage selection code and MemoryStorage.
With enough finagling you could multiplex storages, perhaps by writing your own storage multiplexer composite:
class StorageMultiplexer {
constructor(...storageBackends) {
this._storages = storageBackends;
}
_handleFile(req, file, cb) {
this._storages.forEach(s => s._handleFile(req, file, cb));
}
_removeFile(req, file, cb) {
this._storages.forEach(s => s._removeFile(req, file, cb));
}
}
var diskStorage = require('./storage/disk')
var memoryStorage = require('./storage/memory')
const upload = multer({
dest: 'uploads/',
storage: new StorageMultiplexer(diskStorage, memoryStorage)
});
This code is not tested and is merely a demonstration
Please read StorageEngine.md for more direction.
I am trying to upload a file image with multer in express and file is uploading in the directory, but name of the file is not saving in database. I am using mongodb with express. Filename is saving as noimage.png.
routes/posts.js:-
router.post('/add', function(req, res, next) {
if(req.files.mainimage){
console.log('Uploading files...');
// File Info
var mainImageOriginalName = req.files.mainimage.originalname;
var mainImageName = req.files.mainimage.name;
var mainImageMime = req.files.mainimage.mimetype;
var mainImagePath = req.files.mainimage.path;
var mainImageExt = req.files.mainimage.extension;
var mainImageSize = req.files.mainimage.size;
}
else{
var mainImageName = 'noimage.png';
}
//console.log(req.files.mainimage.name);
// Check for errors
var errors = req.validationErrors();
if(errors){
res.render('add', {
errors: errors,
});
}
else{
var posts = db.get('posts');
// Submit to db
posts.insert({
mainimage: mainImageName
}, function(err, post){
if(err){
res.send('There was an issue submitting the post');
}
else{
req.flash('success', 'Post Submitted');
res.location('/posts');
res.redirect('/posts');
}
});
}
});
If you are using multer it seems you didn't specify as multer's specification
router.post('/add', multer({ dest: './uploads/'}).single('myimage'), function(req,res){...})
I want to upload image using a jade form then i want to show it in a posts page the problem is the images are uploaded as files with no extension and then i get Cannot read property 'mainimage' of undefined
My app.js code is below
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './public/images/uploads/');
},
filename: function (req, file, callback) {
callback(null, file.fieldname + '-' + Date.now());
}
});
var upload = multer({storage: storage}).single('mainimage');
Then my posts.js
router.post('/add', function(req, res, nect){
// Get The Form Values
var title = req.body.title;
var category = req.body.category;
var body = req.body.body;
var author = req.body.author;
var date = new Date();
if(req.file.mainimage){
var mainImageOriginalName = req.file.mainimage.originalname;
var mainImageName = req.file.mainimage.name;
var mainImageMime = req.file.mainimage.mimetype;
var mainImagePath = req.file.mainimage.path;
var mainImageExt = req.file.mainimage.extension;
var mainImageSize = req.file.mainimage.size;
} else{
var mainImageName = 'noimage.png';
}
//Form Validation
req.checkBody('title', 'Title field is required').notEmpty();
req.checkBody('body', 'Body field is required');
//Check errors
var errors = req.validationErrors();
if(errors){
res.render('addpost',{
"errors": errors,
"title": title,
"body": body
});
} else{
var posts = db.get('posts');
//submit to db
posts.insert({
"title": title,
"body": body,
"category": category,
"date": date,
"author": author,
"mainimage": mainImageName
}, function(err, post){
if(err){
res.send('There was an issue submitting the post');
} else{
req.flash('success', 'Post Submitted');
res.location('/');
res.redirect('/');
}
});
}
});
And this my posts.jade Form
form(method='post', action='/posts/add', enctype='multipart/form-data')
.form-group
label Title:
input.form-control(name='title', type='text')
.form-group
label Category
select.form-control(name='category')
each category, i in categories
option(value='#{category.title}') #{category.title}
.form-group
label Body
textarea.form-control(name='body', id='body')
.form-group
label Main Image:
input.form-control(name='mainimage', type='file', id='mainimage')
And here is where i want to display it
each post, i in posts
.posts
h1
a(href='/posts/show/#{post._id}')
=post.title
p.meta Posted in #{post.category} by #{post.author} on #{moment(post.date).format('MM-DD-YYYY')}
img(src='/images/uploads/#{post.mainimage}')
!=post.body
a.more(href='/posts/show/#{post._id}') Read More
The extension problem is probably because in your storage you name your file without the extension.
To add it, you can insert this code in your storage method.
filename: function (req, file, callback) {
var extension = file.mimetype;
extension = extension.substring(extension.indexOf("/")+1, extension.length);
var filename = file.fieldname + '-' + Date.now() + "." + extension;
callback(null, filename);
}
Also, if you don't have your directory mapped, you can insert in you app.js a static mapping to the folder where you saved the file, like this:
app.use('/images', express.static(path.join(__dirname, 'images')));
Then, in your post page you can access the downloaded file directly via its URL, using http://yourdomain:port/images/<filename.extension>.
Hope it helps.
I´m storing images from my angular app in MongoDB using GridFs. But i cant figure out, how to GET the images out of the DB to the app?
I´m using a custom objectId for the query.
EDIT
It looks like the GET part now works, but then there was no media in the collection. I played a bit with the code, and now I can see fs.chunks and fs.files in the database. I think the problem is, that I try to query for metadata in the GET request. This returns no response data. Anybody got an idea how to fix this?
var fs = require('fs');
var conn = mongoose.connection;
var Grid = require ('gridfs-stream');
Grid.mongo = mongoose.mongo;
var gfs = Grid (conn.db);
var buffer = "";
app.post('/uploads/', multer({
upload: null,
onFileUploadStart: function (file, req){
this.upload = gfs.createWriteStream({
filename: file.originalname,
metadata:{"objectId" : req.body.project_id},
mode: "w",
chunkSize: 1024*4,
content_type: file.mimetype,
root: "fs",
});
},
onFileUploadData: function(file, data) {
this.upload.write(data);
},
onFileUploadComplete: function(file, res) {
done=true;
}
}), function(req, res){
res.status(200);
res.send("Success!");
});
app.route('/uploads/media/:projectId').get(function (req, res){
var readstream = gfs.createReadStream({
"metadata.objectId" : req.params.projectId
});
res.set('Content-Type', 'image/jpeg');
readstream.pipe(res);
});
You need to write the stream back out to your response. Here is another similar question. But basically you either need to pipe the stream to your response, or use the stream's end event and write the result to your response. The following code pipes to the response and sets a content-type of image/jpeg.
app.get('/uploads/:objectId', function(req, res){
var options = {
_id : req.params.objectId
};
gfs.exist(options, function(err, exists) {
if(!exists) {
res.status(404);
res.end();
} else {
var readstream = gfs.createReadStream(options);
res.set('Content-Type', 'image/jpeg');
readstream.pipe(res);
}
});
});
var pi_id = fields.pic_id;
gfs.findOne({ _id: pi_id }, function (err, file) {
console.log(file);
if (err) return res.status(400).send(err);
if (!file) return res.status(404).send('');
res.set('Content-Type', file.contentType);
res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
var readstream = gfs.createReadStream({
_id: file._id
});
readstream.on("error", function(err) {
console.log("Got error while processing stream " + err.message);
res.end();
});
readstream.pipe(res);
console.log(readstream.pipe(res))
});