node eggjs 上传文件中转代理过河

0.前面

技术栈:eggjs + axios + request + vue

真的是摸着石头过河,对技术栈的源码、API不熟悉,不知道能功能可以实现到什么程度。只能一步一步的“写代码 -> 运行代码 -> 测试功能是否实现”,就像胡乱建一栋房子一样,可以保证建的时候不塌,至于以后塌不塌、漏不漏风,就不清楚。只能在使用过程中,不断维护了。

1.第一个版本(eggjs + axios)

async upload() {
    const { logger, service, ctx } = this;
    const stream = await ctx.getFileStream();
    try {
      const { query } = URL.parse(ctx.url, true);
      const name = query.name || stream.fields.name || stream.filename;
      const md5 = query.md5 || stream.fields.md5;
      const pid = query.pid || stream.fields.pid;
      const formData = new FormData();
      formData.append('name', name);
      formData.append('md5', md5);
      formData.append('pid', pid);
      formData.append('file', stream, {
        filename: name,
        header: `--${formData.getBoundary()}\r\nContent-Disposition: form-data; name="file"; filename=${name}\r\nContent-Type: octet-stream\r\n\r\n`
      });
      const options = {
        method: 'post',
        data: formData,
        headers: {
          'content-type': 'multipart/form-data; boundary=' + formData.getBoundary()
        }
      };
      return await this.request(options);
    } catch (e) {
      logger.debug(e);
      ctx.response.status = 400;
      ctx.body = {
        code: 400,
        message: '上传失败:未知错误!',
      };
    } finally {
      // must consume the file stream, or the browser will get stuck
      if (stream) { await sendToWormhole(stream); }
    }
  }

这里出错了很久,eggjs的ctx.getFileStream()经过了处理,FormData添加file字段的时候总是报错。研究源码,不断测试,发现自动处理header出错,后来手动在file字段加上header才成功上传。

formData.append('file', stream, {
  filename: name,
  header: `--${formData.getBoundary()}\r\nContent-Disposition: form-data; name="file"; filename=${name}\r\nContent-Type: octet-stream\r\n\r\n`
});

后来,测试发现不支持上传大文件(一百多M),上传十几M就失败停止,应该是分块上传的时候,没有处理后面的数据。实在不知道axios是怎么处理这种情况的,没办法,只能使用其他技术。

2.第二个版本(eggjs + request)


  async upload() {
    const { logger, config, ctx } = this;
    let url = ctx.url.replace(/^\/api/, '');
    url = (config.api.host || 'http://127.0.0.1:8080') + url;
    logger.info(`post ${url}`);
    const func = (resolve, reject) => {
      const x = request(url, (error, response, data) => {
        if (!error && response.statusCode === 200) {
          response.data = data;
          resolve(response);
        } else {
          reject({
            code: 'error',
            result: 'fail',
            message: 'API服务器异常'
          });
        }
      });
      ctx.req.pipe(x); // 核心语句
    };
    ctx.body = await new Promise(func)
      .then(response => {
        return response.data;
      })
      .catch((error) => {
        logger.error('API服务器异常', error && error.code, error && error.message);
        return error;
      });
  }

一开始使用http-proxy,但不支持eggjs,以下代码是返回不了数据的,不过现在想想,应该给ctx.res加个data事件处理就行了。

proxy(ctx.req, ctx.res, { target });

然后又找到egg-proxy,但这是个插件,不符合我的需求,所以我就研究他的源码,看他怎么解决文件上传代理的。一路找到request(egg-proxy -> koa-proxy -> co-request -> request),对于co-request实在看不明白,在eggjs下,以下代码的res是个方法,获取不了数据的,应该是需要增加一些异步处理关键字。

改为eggjs环境源码(res是方法,获取不了数据)koa-proxy相关源码
const requestThunk = request(opt);

let res;
if (parsedBody) {
  res = requestThunk;
} else {
  // Is there a better way?
  // https://github.com/leukhin/co-request/issues/11
  res = pipeRequest(this.req, requestThunk);
}

ctx.status = res.statusCode;
ctx.body = res.body;
var requestThunk = request(opt);

if (parsedBody) {
  var res = yield requestThunk;
} else {
  // Is there a better way?
  // https://github.com/leukhin/co-request/issues/11
  var res = yield pipeRequest(this.req, requestThunk);
}

ctx.status = res.statusCode;
ctx.body = res.body;

最后只能使用request,并加上promise异步处理。

3.第三个版本(eggjs + axios)

还是想用回axios,毕竟突然出现一个repuest,强迫症受不了。

目前已经有了一点思路,Ability to pipe read stream to PUT/POST request (Node) #553

转载于:https://my.oschina.net/xiaobingchuanqi/blog/3013189

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值