Upload image + duplicate(low-res thumbnail) via multerjs - node.js

I am building a webapplication that allows users to upload an avatar. However, in addition to the avatar I want to create a thumbnail low-res (e.g. 64 x 64) and store it on my server. Currently I'm using this code to store the image in two directories, but it's the same resolution in both directories. How would I store a different resolution in the thumbnails directory?
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './uploads/thumbs');
callback(null, './uploads');
},
filename: function (req, file, callback) {
callback(null, file.fieldname + '-' + Date.now());
}
});

You can use sharp for resize you images in your 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))
}
})
and:
router.post('/', upload.single('image'), async (req, res) => {
try {
const { filename: image } = req.file
await sharp(req.file.path, { failOnError: false })
.resize(64, 64)
.withMetadata()
.toFile(
path.resolve(req.file.destination, './thumbs', image.replace(/\.(jpeg|png)$/, `.jpg`))
)
fs.unlink(req.file.path)
return res.json({
thumb_url: '/uploads/thumbs/' + image.replace(/\.(jpeg|png)$/, `.jpg`)
});
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error!');
}
});
fs.unlink(req.file.path) remove your orginal image from ./uploads

Related

Node.js - Uploading both an image and a file with Multer?

I am working on a mod site, and I was able to create a method that uploads an image, but I was wondering if it's possible to also upload a zip file in the same method. I tried chaining on code for the file after creating a file model, but I get an unexpected field error. Here's the method I'm trying
router.post('/upload', uploadImage.single('image'), uploadFile.single('file'), IsLoggedIn, (req, res, next) => {
console.log(req.file);
var newImg = new Image({
data: req.file.filename
});
var newFile = new File({
data: req.file.filename
});
Mod.create({
name: req.body.name,
image: newImg.data,
game: req.body.game,
file: newFile.data
}, (err, newMod) => {
if (err) {
console.log(err);
}
else {
res.redirect('/mods');
}
});
});
And here are my storage engines
const imageStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './public/images')
},
filename: (req, file, cb) => {
cb(null, file.originalname)
}
});
const fileStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './public/images')
},
filename: (req, file, cb) => {
cb(null, file.originalname)
}
});
const uploadImage = multer({storage: imageStorage});
const uploadFile = multer({storage: fileStorage});
Is it possible to use 2 storage engines and chain 2 uploads in one method, or am I going about it all wrong? Any clarification or direction would be greatly appreciated.

how to create year/month/day structure when uploading files with nodejs multer?

I want to upload the file using multer into folder structure with year/month/day.
Like upload/2021/06/27/filename. How can I do that?
//configuring multer storage for images
const fileStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'upload/');
},
filename: (req, file, cb) => {
cb(null, new Date().toISOString().replace(/:/g, '-') + '-' + file.originalname);
}
});
You can create a custom function using fs library functions,
initialize fs lib
const fs = require("fs");
create a method to return date path on the base of input param current date
ex:
input: new Date()
return: "2021/6/27"
function getDatePath(date) {
return date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate();
}
create directory recursive if it does not exist, can handle with try-catch block
function getDirPath(dirPath) {
try {
if (!fs.existsSync(dirPath)) fs.promises.mkdir(dirPath, { recursive: true });
return dirPath;
} catch (error) {
console.log(error.message);
}
}
use the above method in destination
//configuring multer storage for images
const fileStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, getDirPath('upload/' + getDatePath(new Date())));
},
filename: (req, file, cb) => {
cb(null, new Date().toISOString().replace(/:/g, '-') + '-' + file.originalname);
}
});

Image is getting saved as id while using multer

I am trying to upload an image using multer but the images gets saved as an Id .
I have tried to set the file name to original name still the name of file doesn't change
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads')
}},{
filename: function (req, file, cb) {
cb(null, file.originalname + '-' + Date.now())
}
})
var upload = multer({ storage: storage })
let PhotoModel = require('../models/photo')
router.post('/',upload.single('image'),(req, res, next) => {
let msg = new PhotoModel(
{images: req.file.originalname}
)
msg.save()
.then(doc => {
console.log(doc)
})
.catch(err => {
console.error(err)
})
res.send(msg)
})

How to resize image size in nodejs using multer

Multer have already limit size property. This property only restrict the image. Not resize the image. My question is suppose image is greater than "limit size", how to resize that image ?
var storageOptions = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'useravatars/')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
});
var avatarUpload = multer({
storage: storageOptions,
limits: {
fileSize: 1000000
}
}).single("avatar");
It depends on whether you want to store the resized image as well.
In any case, you'll use a library to handle the resize operation. sharp is a very good option.
Resize in a route handler(after file is stored to disk):
sharp(req.file).resize(200, 200).toBuffer(function(err, buf) {
if (err) return next(err)
// Do whatever you want with `buf`
})
Other option would be creating your own storage engine, in this case you'll receive the file data, resize, then store to disk (copied from https://github.com/expressjs/multer/blob/master/StorageEngine.md):
var fs = require('fs')
function getDestination(req, file, cb) {
cb(null, '/dev/null')
}
function MyCustomStorage(opts) {
this.getDestination = (opts.destination || getDestination)
}
MyCustomStorage.prototype._handleFile = function _handleFile(req, file, cb) {
this.getDestination(req, file, function(err, path) {
if (err) return cb(err)
var outStream = fs.createWriteStream(path)
var resizer = sharp().resize(200, 200).png()
file.stream.pipe(resizer).pipe(outStream)
outStream.on('error', cb)
outStream.on('finish', function() {
cb(null, {
path: path,
size: outStream.bytesWritten
})
})
})
}
MyCustomStorage.prototype._removeFile = function _removeFile(req, file, cb) {
fs.unlink(file.path, cb)
}
module.exports = function(opts) {
return new MyCustomStorage(opts)
}
const path = require("path");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, path.join(__dirname, "/uploads"));
},
filename: function (req, file, cb) {
cb(null, uuid.v4() + `${path.extname(file.originalname)}`);
}
});
const limits = {
fields: 10,
fileSize: 500 * 1024,
files: 1,
};
const upload = multer({ storage, limits });
const baseUrl = "http://localhost:3000/files/";
router.post("/upload", upload.single("file"), async (ctx, next) => {
ctx.body = {
code: 1,
data: baseUrl + ctx.file.filename,
};
});

Rename uploaded images using MULTER

I am uploading the images using multer. They all are given random names (dec93b9f333c7a731723b06ce73c0bbc.jpg), which is very bad for SEO... Can you guys help me out, how to save the images with the pattern: 'fixed-name'+'random-name'.extension. Then at least part of the file would be readable for the google. Thanks!
app.set('images', '/var/www/images');
app.use(app.get('images'), express.static(app.get('images')));
var multerForImage = multer({
dest: app.get('images'),
onParseStart: function (file) {
console.log("Started parsing file stream", file);
},
onFileUploadStart: function (file) {
console.log('File recieved: ', file.originalname);
},
onFileUploadComplete: function (file, req, res) {
console.log("File upload complete");
var path = app.get('images') + "/" + file.name;
var user = req.session.user;
res.json({
success: true,
data: path
});
},
onFileUploadData: function (file, data, req, res) {
console.log('Data recieved for file upload');
},
onParseEnd: function (req, next) {
console.log("Parsing data end for file upload");
}
});
You can use the storage configuration.
app.set('images', '/var/www/images');
app.use(app.get('images'), express.static(app.get('images')));
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, app.get('images'))
},
filename: function (req, file, cb) {
const randomPart = uuidV4(); // use whatever random you want.
const extension = file.mimetype.split('/')[1];
cb(null, 'fixed-name'+ randomPart + `.${extension}`)
}
})
var multerForImage = multer({
storage: storage,
...

Resources