Transform images into thumbnails before uploading to S3 in nodejs - node.js

I have been trying multer-s3 for many hours, But i'm failing in creating thumbnails. Can anyone please tell me how to run it?
Following is my code:
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'test',
// shouldTransform: function (req, file, cb) {
// cb(null, /^image/i.test(file.mimetype))
// },
acl: 'public-read',
contentType: multerS3.AUTO_CONTENT_TYPE,
shouldTransform : function (req, file, cb) {
console.log('in should transform ', file)
cb(null, /^image/i.test(file.mimetype))
},
transforms: [{
id: 'original',
key: function (req, file, cb) {
console.log('original')
cb(null, "original")
},
transform: function (req, file, cb) {
console.log('original1')
cb(null, sharp().jpg())
}
}, {
id: 'thumbnail',
key: function (req, file, cb) {
console.log('thumbnail')
cb(null, "thumbnail")
},
transform: function (req, file, cb) {
console.log('thumbnail1')
cb(null, file.resize(100, 100).jpg())
}
}]
})
})
app.post('/upload', upload.single('image'), extendTimeout, function(req, res, next) {
console.log('filessss ', req.file )
res.send('Successfully uploaded ' + req.file + ' files!')
})
Original image is uploaded successfully but resized image is not. Can anyone please guide me?
Thanks

There is a typo in thumbnail's transform method.
Instead of
cb(null, file.resize(100, 100).jpg())
should be
cb(null, sharp().resize(100, 100).jpg())

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.

save image to s3 bucket with image formate

I am trying to upload an image to a s3 bucket using multerS3. It does save the file to the s3 bucket but not in an image format.
here is my code.
storage: multerS3({
s3: S3,
bucket: 'slsupload',
acl: 'public-read',
metadata: function (req, file, cb) {
cb(null, {fieldName: file.fieldname});
},
key: function (req, file, cb) {
cb(null, Date.now().toString()+".jpg")
}
})
});
const singleUpload = upload.single('file');
app.post('/test-upload', (req, res) => {
singleUpload(req, res, function(err, some) {
if (err) {
return res.status(422).send({errors: [{title: 'Image Upload Error', detail: err.message}] });
}
return res.json({'imageUrl': req.file.location});
});
});```
You can try setting the contentType buy hand or let multerS3 discover the contentType automatically:
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
contentType: multerS3.AUTO_CONTENT_TYPE,
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})

How to receive req.file from request form-data in node

I have two apis, I want to send a file from the first api using request and formData.
How to receive req.file from request form-data ?
Receive side code
var storage = multer.diskStorage({
destination: function (req, file, cb) {
mkdirp(configServer.dataDir+ "/tmp", function(err){
cb(null, configServer.dataDir+ "/tmp/")
})
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload = multer({ storage: storage }).single('file');
exports.upload_in_server = function (req, res) {
upload(req, res, function (err) {
console.log("file : ", req.file)
console.log("body : ", req.body)
res.json({success: true})
})
}
router.post("/myurl/uploadInServer", UserController.upload_in_server);
Send side code
var storage2 = multer.diskStorage({
destination: function (req, file, cb) {
mkdirp(config.dataDir+ "/tmp", function(err){
cb(null, config.dataDir+ "/tmp/")
})
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload2 = multer({ storage: storage2 }).single('file');
exports.user_coffre_fort_create_file2 = function (req, res) {
upload2(req, res, function (err) {
var obj = {
'Nom': "Lagaf",
'Prénom': "Vincent",
'Date de naissance': "13/01/1960",
'file':new Buffer(fs.readFileSync(req.file.path)).toString("base64")
}
request({
url: "/myurl/uploadInServer",
method: 'POST',
formData: obj,
headers: {"Content-Type": "application/x-www-form-urlencoded", "Authorization": token}
}, function (err, stdout, body) {
res.json({success:true})
})
})
}
This is what I receive
the req.file is null, and i received the file in the body
How to recevie the file in the req.file ?
Thanks in advance
file : undefined
body : {
'Nom: 'Lagaf',
'Prénom': 'Vincent',
'Date de naissance': '13/01/1960',
file:/9j/4gIcSUNDX1BST0ZJTEUAAQEAAAIMbGNtcwIQAABtbnRyUkdCIFhZWiAH3AABABkAA
You need to use multipart/form-data as content type for uploading files and form feature from requests module. Take a look at this answer.

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

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

Transform upload with NodeJS Multer

I'm currently implementing a file/image upload service for my users. I want to transform these images (resize/optimize) before uploading to my s3 bucket.
What I'm currently doing: Using a multipart form on my frontend (I think the actual implementation doesn't matter here..) and the multer and multer-s3 packages on my backend.
Here my implementation stripped down to the important parts.
// SETUP
var multer = require('multer');
var s3 = require('multer-s3');
var storage = s3({
dirname: 'user/uploads',
bucket: auth.aws.s3.bucket,
secretAccessKey: auth.aws.s3.secretAccessKey,
accessKeyId: auth.aws.s3.accessKeyId,
region: auth.aws.s3.region,
filename: function (req, file, cb) {
cb(null, Date.now());
}
});
var upload = multer({storage: storage}).single('img');
// ROUTE
module.exports = Router()
.post('/', function (req, res, next) {
upload(req, res, function (err) {
if (err) {
return res.status(401).json({err: '...'});
}
return res.json({err:null,url: '..'});
});
});
What I want to do: transform the image before uploading it. I'm not sure if I need to use multer/busboy here or I can just do it with NodeJS (thus I've tagged NodeJS and express as well).
So my question is: where can I intercept the upload and transform it before uploading it to my S3 bucket?
Not sure if you're still looking for an answer to this, but I had the same problem. I decided to extend the multer-s3 package.
I've opened a pull request to the original repository, but for now, you can use my fork.
Here's an example of how to use the extended version:
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
shouldTransform: function (req, file, cb) {
cb(null, /^image/i.test(file.mimetype))
},
transforms: [{
id: 'original',
key: function (req, file, cb) {
cb(null, 'image-original.jpg')
},
transform: function (req, file, cb) {
cb(null, sharp().jpg())
}
}, {
id: 'thumbnail',
key: function (req, file, cb) {
cb(null, 'image-thumbnail.jpg')
},
transform: function (req, file, cb) {
cb(null, sharp().resize(100, 100).jpg())
}
}]
})
})
EDIT: My fork is also now available via npm under the name multer-s3-transform.
I've tried using #ItsGreg's fork, but couldn't get it to work. I managed to get this behaviour working by using multer-s3 standard configuration, and inside my file upload endpoint, i.e.,
app.post('/files/upload', upload.single('file'), (req, res) => {...})
I am retrieving the file using request, and passing the Buffer to sharp. The following works (and assumes you are using ~/.aws/credentials):
let request = require('request').defaults({ encoding: null });
let dataURI = `https://s3.amazonaws.com/${process.env.AWS_S3_BUCKET}/${image.defaultUrl}`;
request.get(dataURI, function (error, response, body) {
if (! error && response.statusCode === 200) {
let buffer = new Buffer(body);
const sizes = ['thumbnail', 'medium', 'large'];
sizes.forEach(size => {
sharp(buffer)
.resize(image.sizes[size])
.toBuffer()
.then(data => {
// Upload the resized image Buffer to AWS S3.
let params = {
Body: data,
Bucket: process.env.AWS_S3_BUCKET,
Key: `${image.filePath}${image.names[size]}`,
ServerSideEncryption: "AES256",
};
s3.putObject(params, (err, data) => {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
})
})
}
});

Resources