Handling multipart/form-data POST with Express in Cloud Functions
I've combined the previous two answers into a easy-to-use async function.
const Busboy = require('busboy');
const os = require('os');
const path = require('path');
const fs = require('fs');
module.exports = function extractMultipartFormData(req) {
return new Promise((resolve, reject) => {
if (req.method != 'POST') {
return reject(405);
} else {
const busboy = new Busboy({ headers: req.headers });
const tmpdir = os.tmpdir();
const fields = {};
const fileWrites = [];
const uploads = {};
busboy.on('field', (fieldname, val) => (fields[fieldname] = val));
busboy.on('file', (fieldname, file, filename) => {
const filepath = path.join(tmpdir, filename);
const writeStream = fs.createWriteStream(filepath);
uploads[fieldname] = filepath;
file.pipe(writeStream);
const promise = new Promise((resolve, reject) => {
file.on('end', () => {
writeStream.end();
});
writeStream.on('finish', resolve);
writeStream.on('error', reject);
});
fileWrites.push(promise);
});
busboy.on('finish', async () => {
const result = { fields, uploads: {} };
await Promise.all(fileWrites);
for (const file in uploads) {
const filename = uploads[file];
result.uploads[file] = fs.readFileSync(filename);
fs.unlinkSync(filename);
}
resolve(result);
});
busboy.on('error', reject);
if (req.rawBody) {
busboy.end(req.rawBody);
} else {
req.pipe(busboy);
}
}
});
};
The documentation Doug referred to in his answer is good. An important caveat though is that rawBody
does not work in the emulator. The workaround is to do:
if (req.rawBody) {
busboy.end(req.rawBody);
}
else {
req.pipe(busboy);
}
As described in this issue: https://github.com/GoogleCloudPlatform/cloud-functions-emulator/issues/161#issuecomment-376563784
Please read the documentation for handling multipart form uploads.
... if you want your Cloud Function to process multipart/form-data, you can use the rawBody property of the request.
Because of the way Cloud Functions pre-processes some requests, you can expect that some Express middleware will not work, and that's what you're running into.