I have been working for a long time on using google cloud functions with busboy to take an attachment from an email, posted through the Sendgrid Inbound Email Parse API, and storing that attachment in Firebase Storage.
I understand multer may have been easier, but there was a breaking change, so busboy is recommended. (ref: How to perform an HTTP file upload using express on Cloud Functions for Firebase (multer, busboy))
I can't get the below to work to upload attachments:
- If I use busboy.end(req.rawBody) instead of req.pipe(busboy) the function runs, and data is stored to firebase storage, but it does not have a file type. Using req.pipe terminates the function early and I don't see anything running (I have loggers throughout).
- I don't understand what pipe does anyway and haven't been able to find clarity.
- Is busboy the simplest way to handle this? I haven't had luck with Twilio support or their documents, and I thought this use case would be very thoroughly documented.
Thank you for any guidance! I know I'm over my head here.
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const Busboy = require("busboy");
exports.InboundEmail = functions.https.onRequest(async (req,
res) => {
let attachment = { file: null };
let fileData = null;
let dataLength = null;
try {
const busboy = Busboy({
headers: req.headers,
limits: {
fileSize: 10 * 1024 * 1024,
},
});
busboy.on("field", (fieldname, val, fieldnameTruncated,
valTruncated, encoding, mimetype) => {
//Success doing things with code
}
);
busboy.on("file", async (fieldname, file, filename, encoding,
mimetype) => {
file.on("data", function (data) {
if (fileData === null) {
fileData = data;
} else {
fileData = Buffer.toString(data);
}
});
if (attachment.file === null) {
attachment = {
fieldname: fieldname,
file: file,
filename: filename,
encoding: encoding,
mimetype: mimetype,
};
}
file.on("end", function () {
console.log("File [" + fieldname + "] Finished");
});
});
busboy.on("finish", async function () {
attachment.file = fileData;
await uploadToFirebaseStorage(attachment);
next();
});
//This fires before anything runs
req.pipe(busboy);
//using busboy.end(req.rawBody) I get the function running,
and data is stored, but it is not the file;
res.status(200).send("success");
} catch (err) {
res.status(500).send("Error processing email");
}
});
async function uploadToFirebaseStorage(attachment) {
const imageBucket = "files/";
const bucket = admin.storage().bucket();
try {
await bucket
.file("randomname")
.save(attachment.file)
.catch((e) => {
console.log(e);
});
} catch (e) {
throw new Error(e);
}
}