eggjs 如何方便的获取multipart 参数

eggjs 自身的multipart 解析

eggis 有两大特点:

  1. 懒解析:只有在主动调用时, 比如this.multipart, 才会解析multipart 请求。
  2. 流式stream处理:如果想在拿到fd 文件前得到fields, fd 就必须放在fields 之后。

Stream 能让server 性能更好。但是这也给开发带来了不便。

  1. 处理 fd 之前,不能提前获得fields
  2. fd stream 必须消费掉,否则连接不会关闭, 浏览器pending.

统一处理

可以参考下面的方法,统一解析出 fields, 并消费掉stream.

// app/core/base_controller.js
const app = require('egg');

module.exports =
  app.BaseController =
  class BaseController extends app.Controller {
    rest(rest) {
      this.ctx.body = typeof rest === 'object' ? rest : JSON.stringify(rest)
      this.ctx.type = 'application/json';
    }

    throw(msg) {
      msg = msg || 'unknown';
      this.ctx.throw(400, msg);
    }

     async multipart() {
      const tmp = require('tmp')
      const fs = require('fs')
      const ctx = this.ctx
      const files = [];
      ctx.files = files
      let fields, part;
      if (this.ctx.get('Content-Type').startsWith('multipart/')) {
        try {
          // const file = await ctx.getFileStream()
          // files.push(file);
          // fields = stream.fields;
          // const sendToWormhole = require('stream-wormhole');
          // let result = await ctx.oss.put('egg-multipart-test/' + part.filename, part);
          // await sendToWormhole(part);
          const parts = ctx.multipart({ autoFields: true });
          while ((part = await parts()) != null) {
            if (part.length) {
            } else if (part.filename) {
              const tmpFile = tmp.fileSync({prefix: 'eggjs-upload-'})
              const ws = fs.createWriteStream(null, {fd:tmpFile.fd})
              part.pipe(ws)
              part.path = tmpFile.name
              part.fd = tmpFile.fd
              files.push(part)
            }
          }
          fields = parts.field
        } catch (err) {
          //await sendToWormhole(part);
          throw err;
        }
      }
      return [files, fields];
    }
  }

使用时,直接可获取到上传的文件+fields:

      const [files, fields] = await this.multipart();

另外为了及时清理临时文件,我设置了 ctx.files 存放临时文件。
然后通过中间件middleware 在请求最后及时清理:

  if (ctx.files && ctx.files.length) {
    for (let file of ctx.files) {
      if (file.path && fs.existsSync(file.path)) {
        fs.unlinkSync(file.path)
      }
    }
  }

加载baseController

显然,默认情况下,node 不支持项目根目录加载:

require('core/base_controller')

我们需要在app.js 里面设置:NODE_PATH

let path = require('path')
process.env.NODE_PATH = path.resolve(__dirname, 'app/') ;
require('module').Module._initPaths();

或者在命令行直接设置: NODE_PATH=$PWD/app egg-bin dev

vscode 自动补全

vscode 可不知道你设置了NODE_PATH, 所以 vsc 也找不到module. 我们需要再项目根目录中通过jsconfig.json 告诉vsc 去base_dir 找module.

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "baseUrl": "./app"
  },
  "exclude": [
    "node_modules"
  ]
}

你可能感兴趣的:(eggjs,upload)