koajs / joi-router

Configurable, input and output validated routing for koa
MIT License
450 stars 96 forks source link

is there a nice way for await-busboy? #58

Closed willin closed 5 years ago

willin commented 6 years ago
handler: async (ctx) => {
    const parts = ctx.request.parts;
    let part;

    try {
      while ((part = await parts)) {
        // handle files at the first place
        part.pipe(someOtherStream);
      }
    } catch (err) {
    }
    // we need to use fields data first
    console.log(parts.field.name);
  }

we need to use fields data first, such as check username/password.

if the request is valid, then upload file to third party file storage service, like Azure-Blob-Storage

here is a solution that can work:

const Koa = require('koa');
const router = require('koa-joi-router');
const { Writable } = require('stream');
const { getDefer } = require('@dwing/common');

const handleFile = (part) => {
  const deferred = getDefer();
  const stream = new Writable();
  stream.buffers = [];
  stream.length = 0;
  stream._write = function (chunk, encoding, next) {
    this.length = this.length + chunk.length;
    this.buffers.push(chunk);
    next();
  };

  stream.once('finish', function () {
    const buffer = (this.buffers.length === 0 ? new Buffer(0) : (this.buffers.length === 1 ? this.buffers[0] : Buffer.concat(this.buffers, this.length)));
    deferred.resolve(buffer);
  });
  stream.once('error', (err) => {
    deferred.reject(err);
  });

  part.pipe(stream);
  return deferred.promise;
};
const multipart = async (parts) => {
  const result = {
  };
  try {
    let part;
    // eslint-disable-next-line
    while ((part = await parts)) {
      if (part.length) {
        result[part[0]] = part[1];
      } else {
        const field = part.fieldname;
        result[field] = {
          filename: part.filename,
          mime: part.mime,
          mimeType: part.mimeType,
          encoding: part.encoding
        };
        const file = await handleFile(part);
        result[field].length = file.length;
        result[field].buffer = file;
      }
    }
  } catch (err) {
    // handle the error
  }
  return result;
};

const app = new Koa();
const routes = router();
routes.route([{
  method: 'post',
  path: '/upload',
  validate: {
    type: 'multipart',
    maxBody: '5mb'
  },
  handler: async (ctx) => {
    const parts = ctx.request.parts;
    const body = {
      ...await multipart(parts),
      ...parts.field
    };
    console.log(body);
    ctx.body = '1';
  }
}]);

app.use(routes.middleware());
app.listen(3000);

but we wonder if there is a better way, that can copy filestream for later use..

aheckmann commented 5 years ago

closing due to age. please reopen if necessary.