multer not storing image to folder - node.js

I deployed my express app on heroku and am attempting to upload images using multer. This all works perfectly on localhost but on heroku when I upload an image it will not save to my images folder, and I get a 500 server error with:
Error: ENOENT: no such file or directory, open '../images/kauai-mountain.jpg'
I am using create react app on front end.
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(error, '../images/');
},
filename: (req, file, cb) => {
cb(null, file.originalname);
}
});
router.post('/', multer({ storage }).single('image'), (req, res) => {
const { description, category } = req.body;
const url = req.protocol + '://' + req.get('host');
const newPost = new Post({
description,
category,
imagePath: url + '/images/' + req.file.filename
});
newPost.save().then(post => res.json(post)).catch(e => res.json(e));
});
server.js
if (process.env.NODE_ENV === 'production') {
app.use('/images', express.static(path.join(__dirname, 'images')));
app.use('/', express.static('client/build'));
app.get('*' , (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
My front end code is uploading the image and sending it to the route, the error is in the back end.
EDIT
I am reading that the problem may be that heroku cant save images consistently. I don't understand because I am saving them in an images folder on my server which I run on heroku. Could this be the problem?

Related

Multer Image Not loaded on Production Build React?

I am using MERN to create simple eCommerce I am facing error when i am in production mode. The file uploads on the specified folder but don't get load on front-end.
Server.js
import path from 'path'
import express from 'express'
import dotenv from 'dotenv'
const __dirname = path.resolve()
if (process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, '/frontend/build')))
app.get('*', (req, res) =>
res.sendFile(path.resolve(__dirname, 'frontend', 'build', 'index.html'))
)
} else {
app.get('/', (req, res) => {
res.send('Api running....')
})
}
app.use('/uploads', express.static(path.join(__dirname, '/uploads')))
Upload routes and code
import path from 'path'
import express from 'express'
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,
`${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`
)
},
})
function checkFileType(file, cb) {
const filetypes = /jpg|jpeg|png/
const extName = filetypes.test(path.extname(file.originalname).toLowerCase())
const mimeType = filetypes.test(file.mimetype)
if (extName && mimeType) {
return cb(null, true)
} else {
cb('Images only with jpg, jpeg, png ')
}
}
const upload = multer({
storage,
fileFilter: function (req, file, cb) {
checkFileType(file, cb)
},
})
router.post('/', upload.single('image'), (req, res) => {
res.send(`/${req.file.path}`)
})
export default router
When i inspect the file it says on the src the image cant load check the image=>
Now when i am in development mode the same code works as expected and the image loads.
What might the possible error please help me. Thank you in Advance.
Your production server is not meant to store files because that will lead to increase in consumption of server resources. So it will automatically delete any files/images that you have uploaded to the production server.
And why would you want to store files on production server, just use a database or file storage system like s3 bucket.
I got the same error and what I did is just switching the place the of the upload folder on the top. You want to make sure that all the image be available before the build of frontend request any image from it.
import path from 'path'
import express from 'express'
import dotenv from 'dotenv'
const __dirname = path.resolve()
// Put it here
app.use('/uploads', express.static(path.join(__dirname, '/uploads')))
if (process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, '/frontend/build')))
app.get('*', (req, res) =>
res.sendFile(path.resolve(__dirname, 'frontend', 'build', 'index.html'))
)
} else {
app.get('/', (req, res) => {
res.send('Api running....')
})
}

nodejs multer image upload : retain file name and extension

I have a node.js express code below to upload a image and store into a default folder.
I realised that the file gets renamed and the extension is lost. can some one help me fix this error?
1.How to retain extension and file name
if a zip file is upload, i want to unzip it and upload it
const __basefolder = "C:/Users/user/Desktop";
const express = require('express');
const multer = require('multer');
const upload = multer({dest: __basefolder + '/uploads/images'});
const app = express();
const PORT = 3000;
app.use(express.static('public'));
app.post('/upload', upload.single('file'), (req, res) => {
if(req.file) {
res.json(req.file);
}
else throw 'error';
});
app.listen(PORT, () => {
console.log('Listening at ' + PORT );
});
You can define a filename-option in your disk-storage setup, which lets you choose your filename arbitrarily. Since you want the original file-name, you can use file.originalname (note that using this an existing file with the same name will be overwritten though):
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, __basefolder + '/uploads/images');
},
filename: (req, file, cb) => {
cb(null, file.originalname);
}
})
const upload = multer({storage});
Regarding the second question: this has been answered before, see this or that for example.

Upload file to MongoDB and to a local server with the same POST request

I'm trying to make my own upload service, and I want to be able to upload a file in a local folder on my server but also on my mongoDB cloud service (Atlas).
So far, I've done both services separately and they work fine (I made a Node app for uploading files to mongo Atlas and another Node app for uploading files to the server). But now, I would like to unifiy these two into a single node app, where after each POST request, the file gets sent to both Atlas as well as the local folder. I'm using multer and gridfs.
The first attempt was to make two "file-input" fields on my index.html file, and each of those with two different POST requests: one to '/upload' which sends the file to Atlas and the second one to '/uploaddisk' which sends the file to disk. However, the second post request doesn't work (it throws me the error every time I want to submit my file). Uploading the file to mongoDB seems to work just fine every time.
Any ideas how can I do this on a single POST ? Thank you in advance!
Here is the code that I wrote for my server.js app:
//mongo DATA
const dbURI =
"myc-atlas-credentials";
mongoose.Promise = global.Promise;
// mongoose.connect(bdURI, { useNewUrlParser: true, useUnifiedTopology: true });
const conn = mongoose.createConnection(dbURI, {
useNewUrlParser: true,
useUnifiedTopology: true
});
//init gfs
let gfs;
conn.once("open", () => {
//initialize the stream
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection("uploads");
});
//creating the storage engine for MONGO
const storage = new GridFsStorage({
url: dbURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
const filename = file.fieldname + '-' + Date.now() + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: "uploads"
};
resolve(fileInfo);
});
}
});
const upload = multer({ storage: storage });
//set storage engine with multer for disk
const diskstorage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, path.join(__dirname + '/uploads/'));
},
filename: function(req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
const diskupload = multer({ storage: diskstorage });
//route for POST - upload data to mongo
app.post('/upload', upload.single('file'), (req, res) => {
console.log({ file: req.file });
// res.json({ file: req.file });
res.redirect('/');
});
//route for POST - upload data to disk
app.post('/uploaddisk', diskupload.single('file'), (req, res, next) => {
const file = { file: req.file };
if (!file) {
const error = new Error('Please upload a file');
error.httpStatusCode = 400;
return next(error);
}
res.redirect('/');
});
You can try it like this:
function fileUpload(req, res, next) {
upload.single('file')(req, res, next);
diskupload.single('file')(req, res, next);
next();
}
//route for POST - upload data to mongo
app.post('/upload', fileUpload, (req, res) => {
console.log({ file: req.file });
// res.json({ file: req.file });
res.redirect('/');
});

move and rename image after image upload in Multer

I've developed a small web server to upload pictures.
Now I would like to use the original name of the picture and move the picture into a folder. the name of the folder is in the req.body.
Ok, the upload works, but where is the point to rename oand move the picture?
const express = require('express');
const multer = require('multer');
const upload = multer({ dest: __dirname + '/uploads/images' });
const app = express();
const PORT = 3000;
app.use(express.static('public'));
app.post('/upload', upload.single('image'), (req, res) => {
console.log(req.file.originalname)
console.log(req.body.foldername)
if (req.file) {
res.json(req.file);
}
else throw 'error';
});
app.listen(PORT, () => {
console.log('Listening at ' + PORT);
});
This is your question answer to rename a file before it upload
var storage = multer.diskStorage({
// Where to save
destination: function (req, file, cb) {
cb(null, '/tmp/my-uploads')
},
// File name
filename: function (req, file, cb) {
cb(null, file.originalname) // file.originalname will give the original name of the image which you have saved in your computer system
}
})
var upload = multer({ storage: storage })

How to display uploaded image using multer?

I'm using nodejs as backend and vuejs as front. I've uploaded somes files as test using postman to my backend, but, how could I display these uploaded files using multer?
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
const upload = multer({ storage: storage }).single('logo');
router.post('/logo', auth.authorize, (req, res, next) => {
upload(req, res, (err) => {
});
res.status(200).send('uploaded');
});
I got an image named 'logo-1531296527722' in my api public folder. If I try to use it in my img src (http://localhost:3000/api/company/public/logo-1531296527722) it doesn't displayed. If I try to access this route I got this error Cannot GET /api/company/public/logo-1531296527722. In all questions about that, nodejs has its own html processor like ejs or jade, but I my case, nodejs is just serving my front and all images must be rendered using vuejs.
I dit it!
In my app.js I include app.use('/dist', express.static(path.join(__dirname + '/dist')));

Resources