web-dev-qa-db-fra.com

Analyser les données multipart/form à partir du corps en tant que chaîne sur AWS Lambda

Je suis heureux de voir AWS prend désormais en charge les données multipart/form sur AWS Lambda, mais maintenant que les données brutes sont dans ma fonction lambda, comment puis-je les traiter?

Je vois multiparty est une bonne bibliothèque multipartie dans Node pour le traitement multipart, mais son constructeur attend une requête, pas une chaîne brute.

Le message d'entrée que je reçois sur ma fonction Lambda (après l'application du modèle de mappage de corps) est le suivant:

{ "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 et quelques données de fichier.

Le modèle de mappage de corps que j'utilise est

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

Comment analyser ces données pour enregistrer les champs et les fichiers envoyés à ma fonction Lambda?

12
Diederik

Cela a fonctionné pour moi - en utilisant busboy

crédits dus à Analyse multipart/form-data de Buffer dans Node.js dont j'ai copié l'essentiel.

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 };
14
AvnerSo

En s'appuyant sur la réponse de @AvnerSo: s, voici une version simplifiée d'une fonction qui récupère le corps de la requête et les en-têtes en tant que paramètres et renvoie la promesse d'un objet contenant les champs de formulaire et les valeurs (fichiers ignorés):

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);
});
1
jlaitio

busboy ne fonctionne pas pour moi dans le cas "fichier". Il n'a lancé aucune exception, je ne pouvais donc pas gérer l'exception dans Lambda.

J'utilise aws-lambda-multipart-parser lib n'a pas été aussi difficile. Il analyse simplement les données de event.body et renvoie les données sous forme de tampon ou de texte.

Utilisation:

const multipart = require('aws-lambda-multipart-parser');

const result = multipart.parse(event, spotText) // spotText === true response file will be Buffer and spotText === false: String

Données de réponse:

{
    "file": {
        "type": "file",
        "filename": "lorem.txt",
        "contentType": "text/plain",
        "content": {
            "type": "Buffer",
            "data": [ ... byte array ... ]
        } or String
    },
    "field": "value"
}
1
Long Nguyen

Si vous voulez obtenir un objet prêt à être utilisé, voici la fonction que j'utilise. Il en retourne une promesse et gère les erreurs:

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();
    })
0
nfroidure