Reputation: 151
So, I have this post request which used to work fine but once I upgraded to Node 10 I can't seem to figure out why the file event completely stopped firing...
router.post('/uploadImage', authenticate.FBAuth, (req, res) => {
const busboy = new BusBoy({ headers: req.headers });
let imageFileName;
let imageToBeUploaded = {};
busboy.on('file', function onFile(fieldname, file, filename, encoding, mimetype) {
console.log('onFile started');
if (mimetype !== 'image/png' && mimetype !== 'image/jpeg') {
return res.status(400).json({ error: 'Unsupported file format' });
}
const imageExtension = path.extname(filename);
imageFileName = `${Math.round(Math.random() * 100000000000)}.${imageExtension}`;
console.log(imageFileName);
const filepath = path.join(os.tmpdir(), imageFileName);
console.log(filepath);
imageToBeUploaded = { filepath, mimetype };
console.log(imageToBeUploaded);
file.pipe(fs.createWriteStream(filepath));
});
busboy.on('finish', function onFinish() {
admin
.storage()
.bucket(config.storageBucket)
.upload(imageToBeUploaded.filepath, {
resumable: false,
metadata: {
metadata: {
contentType: imageToBeUploaded.mimetype
}
}
})
.then(() => {
const imageUrl = `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${imageFileName}?alt=media`;
return db.doc(`/users/${req.user.handle}`).update({ imageUrl });
})
.then(() => {
return res.json({ message: 'Image uploaded successfully' });
})
.catch(error => {
console.error(error);
return res.status(500).json({ error: error.code });
});
});
busboy.end(req.rawBody);
});
I tried to do console.log(imageToBeUploaded);
inside onFinish and it is always an empty object. Besides, any console logs inside onFile never seem to print.
I also tried without authenticate.FBAuth middleware but that didn't make any difference.
This is the error from bucket().upload()
basically complaining that the path is invalid which makes sense since imageToBeUploaded
is an empty object and hence imageToBeUploaded.filepath
is undefined
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined
at validateString (internal/validators.js:121:11)
at Object.basename (path.js:1156:5)
at Bucket.upload (/Users/mac/Dev/meows/server/node_modules/firebase-admin/node_modules/@google-cloud/storage/build/src/bucket.js:2493:38)
at /Users/mac/Dev/meows/server/node_modules/firebase-admin/node_modules/@google-cloud/promisify/build/src/index.js:69:28
at new Promise (<anonymous>)
at Bucket.wrapper (/Users/mac/Dev/meows/server/node_modules/firebase-admin/node_modules/@google-cloud/promisify/build/src/index.js:54:16)
at Busboy.onFinish (/Users/mac/Dev/meows/server/routes/profileRouter.js:116:14)
at Busboy.emit (events.js:315:20)
at Busboy.emit (/Users/mac/Dev/meows/server/node_modules/busboy/lib/main.js:37:33)
at /Users/mac/Dev/meows/server/node_modules/busboy/lib/types/multipart.js:304:17 {
code: 'ERR_INVALID_ARG_TYPE'
}
I am using Postman for the test and choosing form-data for body and File for the input type. Made sure that Postman handled the Content-Type header setting it to multipart/form-data; boundary=<calculated when request is sent>
Also, this is the file containing authenticate.FBAuth
just in case
const admin = require('firebase-admin');
const db = admin.firestore();
module.exports = {
FBAuth: (req, res, next) => {
let idToken;
if (req.headers.authorization == null || !req.headers.authorization.startsWith('bearer ')) {
console.log('Unrecognized token format');
return res.status(400).json({ error: 'Unauthorized operation' });
}
else {
idToken = req.headers.authorization.split('bearer ')[1];
}
return admin.auth().verifyIdToken(idToken)
.then(decodedToken => {
req.user = decodedToken;
return db.collection('users')
.where('userId', '==', req.user.uid)
.limit(1)
.get()
.then(data => {
req.user.handle = data.docs[0].data().handle;
req.user.imageUrl = data.docs[0].data().imageUrl;
return next();
});
})
.catch(error => {
console.log('Error verifying token ', error);
return res.status(403).json(error);
});
}
}
And here is package.json
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"start": "node index.js"
},
"engines": {
"node": "10"
},
"dependencies": {
"body-parser": "^1.19.0",
"busboy": "^0.3.1",
"express": "^4.17.1",
"firebase": "^7.15.0",
"firebase-admin": "^8.6.0",
"unirest": "^0.6.0"
},
"private": true
}
Upvotes: 4
Views: 1422
Reputation: 151
I fixed it
The problem is that the code was migrated to docker from a cloud function. In the cloud function you use busboy.end(req.rawBody)
since this is where the cloud function stores the request after consuming it. But after migration to docker the request is passed directly to the express route and req.rawBody
is never populated. The solution is to just replace busboy.end(req.rawBody)
with req.pipe(busboy)
Upvotes: 8