Как разобрать тело multipart/form-data с Koa?

Потому что я потратил некоторое (слишком много) время на выяснение этого простого требования. Я документирую здесь способ достижения разбора тела multipart/form-data с Koa.

В моем случае причиной путаницы было количество доступных альтернатив:

И я хотел найти самый минималистский/близкий способ express/koa/node/философию делать вещи.

Так вот оно. Ниже. В принятом ответе. Надеюсь это поможет.

Ответ 1

Вы должны использовать koa-multer, как указано в официальной вики Коа.

Таким образом, простая настройка будет выглядеть так:

const koa = require('koa');
const multer = require('koa-multer');

const app = koa();

app.use(multer());

app.use(function *() {
  this.body = this.req.body;
});

Несколько примечаний:

  • Multer будет анализировать только тела запросов типа multipart/form-data
  • Обратите внимание, что использование this.req.body вместо this.req.body перегружает this.request (не уверен, что это намеренно, но это сбивает с толку наверняка... Я бы ожидал, что разобранное body будет доступно на this.request...)

И FormData эту HTML-форму как FormData:

<form>
  <input type="hidden" name="topsecret" value="1">
  <input type="text" name="area51[lat]" value="37.235065">
  <input type="text" name="area51[lng]" value="-115.811117">
  ...
</form>

Предоставляет вам доступ к вложенным свойствам, как ожидалось:

// -> console.log(this.req.body)
{
  "topsecret": 1,
  "area51": {
    "lat": "37.235065",
    "lng": "-115.811117",
  }
}

Ответ 2

Для Koa2 вы можете использовать async-busboy, поскольку другие решения не поддерживают обещания или async/await.

Пример из документов:

import asyncBusboy from 'async-busboy';

// Koa 2 middleware
async function(ctx, next) {
  const {files, fields} = await asyncBusboy(ctx.req);

  // Make some validation on the fields before upload to S3
  if ( checkFiles(fields) ) {
    files.map(uploadFilesToS3)
  } else {
    return 'error';
  }
}

Ответ 3

Я прошел такое же расследование, как и вы, и здесь есть другие способы получить парсинг тела multipart/form-data с Koa.

co-busboy:

var koa = require('koa');
var parse = require('co-busboy');

const app = koa();

app.use(function* (next) {
  // the body isn't multipart, so busboy can't parse it 
  if (!this.request.is('multipart/*')) return yield next;

  var parts = parse(this),
      part,
      fields = {};
  while (part = yield parts) {
    if (part.length) {
      // arrays are busboy fields 
      console.log('key: ' + part[0]);
      console.log('value: ' + part[1]);

      fields[part[0]] = part[1];
    } else {
      // it a stream, you can do something like:
      // part.pipe(fs.createWriteStream('some file.txt'));
    }
  }

  this.body = JSON.stringify(fields, null, 2);
})

koa-body:

var koa = require('koa');
var router = require('koa-router');
var koaBody = require('koa-body')({ multipart: true });

const app = koa();

app.use(router(app));

app.post('/', koaBody, function *(next) {
  console.log(this.request.body.fields);

  this.body = JSON.stringify(this.request.body, null, 2);
});

В обоих случаях у вас будет ответ вроде:

{
  "topsecret": 1,
  "area51": {
    "lat": "37.235065",
    "lng": "-115.811117",
  }
}

Но лично я предпочитаю, как работает куа-тело. Кроме того, совместим с другим промежуточным программным обеспечением, например, с проверкой каа.

Кроме того, если вы укажете загрузчик в koa-body, он сохранит загруженный файл для вас:

var koaBody = require('koa-body')({
  multipart: true,
  formidable: { uploadDir: path.join(__dirname, 'tmp') }
});