31

I'm glad to see AWS now supports multipart/form-data on AWS Lambda, but now that the raw data is in my lambda function how do I process it?

I see multiparty is a good multipart library in Node for multipart processing, but its constructor expects a request, not a raw string.

The input message I am receiving on my Lambda function (after the body mapping template has been applied) is:

{ "rawBody": "--ce0741b2-93d4-4865-a7d6-20ca51fe2689\r\nContent-Disposition: form-data; name=\"Content-Type\"\r\n\r\nmultipart/mixed; boundary=\"------------020601070403020003080006\"\r\n--ce0741b2-93d4-4865-a7d6-20ca51fe2689\r\nContent-Disposition: form-data; name=\"Date\"\r\n\r\nFri, 26 Apr 2013 11:50:29 -0700\r\n--ce0741b2-93d4-4865-a7d6-20ca51fe2689\r\nContent-Disposition: form-data; name=\"From\"\r\n\r\nBob <[email protected]>\r\n--ce0741b2-93d4-4865-a7d6-20ca51fe2689\r\nContent-Disposition: form-data; name=\"In-Reply-To\"\r... 

etc and some file data.

The body mapping template I'm using is

{ "rawBody" : "$util.escapeJavaScript($input.body).replaceAll("\\'", "'")" } 

How can I parse this data to acecss the fields and files posted to my Lambda function?

4 Answers 4

33
+500

busboy doesn't work for me in the "file" case. It didn't throw an exception so I couldn't handle exception in lambda at all.

I'm using aws-lambda-multipart-parser lib wasn't hard like so. It just parses data from event.body and returns data as Buffer or text.

Usage:

const multipart = require('aws-lambda-multipart-parser'); const result = multipart.parse(event, spotText) // spotText === true response file will be Buffer and spotText === false: String 

Response data:

{ "file": { "type": "file", "filename": "lorem.txt", "contentType": "text/plain", "content": { "type": "Buffer", "data": [ ... byte array ... ] } or String }, "field": "value" } 
Sign up to request clarification or add additional context in comments.

7 Comments

I tried multiparty and was about to try busboy (it looks like they are kind of doing the same stuff) but found your reply and aws-lambda-multipart-parser saved my day, thanks
This saved my day as well. Bounty started is just for this answer - hopefully it'll get some additional upvotes along the way.
I'm getting an empty response: {}. Did you guys deal with the same issue?
If anybody is getting response:{}, try this: if (event.isBase64Encoded) { event.body = Buffer.from(event.body, 'base64').toString(); } let result = multipart.parse(event, true);
"The project is currently closed, and no further updates will be conducted. Please, consider using other libraries." - Anybody know if there is more stable support? Would be nice if AWS added support in their SDK.
|
24

This worked for me - using busboy

credits owed to Parse multipart/form-data from Buffer in Node.js which I copied most of this from.

const busboy = require('busboy'); const headers = { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'OPTIONS, POST', 'Access-Control-Allow-Headers': 'Content-Type' }; function handler(event, context) { var contentType = event.headers['Content-Type'] || event.headers['content-type']; var bb = new busboy({ headers: { 'content-type': contentType }}); bb.on('file', function (fieldname, file, filename, encoding, mimetype) { console.log('File [%s]: filename=%j; encoding=%j; mimetype=%j', fieldname, filename, encoding, mimetype); file .on('data', data => console.log('File [%s] got %d bytes', fieldname, data.length)) .on('end', () => console.log('File [%s] Finished', fieldname)); }) .on('field', (fieldname, val) =>console.log('Field [%s]: value: %j', fieldname, val)) .on('finish', () => { console.log('Done parsing form!'); context.succeed({ statusCode: 200, body: 'all done', headers }); }) .on('error', err => { console.log('failed', err); context.fail({ statusCode: 500, body: err, headers }); }); bb.end(event.body); } module.exports = { handler }; 

Comments

3

Building on @AvnerSo :s answer, here's a simpler version of a function that gets the request body and headers as parameters and returns a promise of an object containing the form fields and values (skipping files):

const parseForm = (body, headers) => new Promise((resolve, reject) => { const contentType = headers['Content-Type'] || headers['content-type']; const bb = new busboy({ headers: { 'content-type': contentType }}); var data = {}; bb.on('field', (fieldname, val) => { data[fieldname] = val; }).on('finish', () => { resolve(data); }).on('error', err => { reject(err); }); bb.end(body); }); 

Comments

1

If you want to get a ready to use object, here is the function I use. It returns a promise of it and handle errors:

import Busboy from 'busboy'; import YError from 'yerror'; import getRawBody from 'raw-body'; const getBody = (content, headers) => new Promise((resolve, reject) => { const filePromises = []; const data = {}; const parser = new Busboy({ headers, }, }); parser.on('field', (name, value) => { data[name] = value; }); parser.on('file', (name, file, filename, encoding, mimetype) => { data[name] = { filename, encoding, mimetype, }; filePromises.push( getRawBody(file).then(rawFile => (data[name].content = rawFile)) ); }); parser.on('error', err => reject(YError.wrap(err))); parser.on('finish', () => resolve(Promise.all(filePromises).then(() => data)) ); parser.write(content); parser.end(); }) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.