Egg: 关于form表单提交file的问题

Created on 7 Mar 2018  ·  27Comments  ·  Source: eggjs/egg

请问form表单提交数据时是否一定要带有file文件上传?对于不带file的form表单,ctx.getFileStream()方法会报错,提示没有发现file文件。ctx.multipart()方法则无法正常继续执行while循环后的代码,

qq 20180307182130
qq 20180307182305
ctx.multipart()是按顺序循环读取流,可是当读取到part.filename==“”(就是没有file文件的时候),就停下来了,while后面的语句不再继续执行。
如果form表单带file文件,则两个方法都是正常的。
请问你们有试过这个问题吗?

Most helpful comment

@WeiGrand https://github.com/eggjs/egg-multipart/pull/17 增加一个函数来更好解决此问题。

All 27 comments

没文件你为啥要调用 ctx.getFileStream()?

直接读 ctx.request.body 即可 https://eggjs.org/zh-cn/basics/controller.html#body

如果是form表单提交的数据,通过ctx.request.body拿到是{},空的。

检查 content-type

content-type是这样的:
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryOdQZ2dV579K3avhs

请问有人遇到这个问题?

你 form 设了 enctype?

设置了,enctype="multipart/form-data"

你们可以用一个form表单,测试一下文档中上传多个文件那段代码,form表单有file,但前端提交的时候不带file文件,
qq 20180308091243
qq 20180308103225

如果没有 file 不用设置 enctype,默认是 application/x-www-form-urlencoded

https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/form

你这个问题是这样的,一般 multipart 方法都是为了取文件,属性是挂载文件 stream 上的,你这样没文件就要单独去取属性,这个是没有例子的,如果你还是想这样做那可以去看下底层代码的实现去取 field。

如果前端只是上传表单的数据,后端如何获取呢?

必须要确保 file 放到最后,其他 form 字段放到 file 的前面,这样 ctx.getFileStream() 拿到的 stream 上会有 fields 字段。

同样遇到了这样的问题 这个异步根本不出while 也就说while外部及后面的代码不会去执行 都不自己去试一下就发各种demo到文档也是醉了

文档上写的清清楚楚,要放在最后一个,文档看不仔细也是醉了。

感谢各位白忙之中的回复,文档写的很清楚,我也看到了,可能是我的问题写的不清楚,让大家误解了。问题不是在于文件的位置,其实文件的位置在fields的第一个还是最后一个都没有关系,都能获取到fields的值。问题在于form-data提交表单的时候,表单中有一些fields,包含一个文件(用户可选是否上传文件),但是当文件是空的时候,表单提交到服务端,服务端如何获取到这些fields。各位有空可以蛮测试一下。enctype="multipart/form-data“形式提交的form-data表单,服务端是以流的形式接收,当文件是空的,ctx.getFileStream()会直接throw err:nodejs.BadRequestError: Can't found upload file。我想知道是我的用法不对,还是form-data提交表单一定要带文件上传。

while后面的代码是否执行就看while里面怎么写,如果if (!part.filename) {return;}注释掉,不论是否有文件都会执行,不是吗? @phper-chen @atian25

@JohnnyMore 注释掉就会报错,不注释就一直while那了

@ JohnnyMore ,这个问题解决了么?我也卡在这个问题上,要是不行的话,只有分开写了。

我也遇到这样的问题,前端页面有4个上传图片的input,网页需求允许用户可以不选择图片上传,这样后端在接收数据的时候就要判断filename是否为空。
我现在的处理方式是如果判读filename为空,那么就执行part.read() 然后继续后面的循环。

好像真有点问题啊!多文件上传,while循环跳不出来啊,官网的例子,如果上传多个图片,有一个图片没为空。而且后续有上传图片的字段,也没挂起了,没能输出来啊

遇到同样的问题,实际项目中肯定会存在文件和其它表单同时提交的场景的,而且文件上传往往也不是必须的,所以解决方法是暂时不要使用 egg 提供的 getFileStream就好

因为看了下 getFileStream 实现的 代码
在最开头就判断了是否有文件上传,没有就直接抛出异常,不太友好

async getFileStream() {
    const parts = this.multipart({ autoFields: true });
    const stream = await parts();
    // stream not exists, treat as an exception
   // 在最开头就判断了 是否有文件上传
    if (!stream || !stream.filename) {
      this.throw(400, 'Can\'t found upload file');
    }
    stream.fields = parts.field;
    stream.once('limit', () => {
      const err = new Error('Request file too large');
      err.name = 'MultipartFileTooLargeError';
      err.status = 413;
      err.fields = stream.fields;
      err.filename = stream.filename;
      if (stream.listenerCount('error') > 0) {
        stream.emit('error', err);
        this.coreLogger.warn(err);
      } else {
        this.coreLogger.error(err);
        // ignore next error event
        stream.on('error', () => {});
      }
      // ignore all data
      stream.resume();
    });
    return stream;
  },

所以实现一个类似的方法自己用就好(其实就是去掉那行是否有文件的判断)

// app/extend/context.js

module.exports = {
  async getFileStreamWithoutFileNotFoundError() {
    const parts = this.multipart({ autoFields: true });
    const stream = await parts();

    stream.fields = parts.field;
    stream.once('limit', () => {
      const err = new Error('Request file too large');
      err.name = 'MultipartFileTooLargeError';
      err.status = 413;
      err.fields = stream.fields;
      err.filename = stream.filename;
      if (stream.listenerCount('error') > 0) {
        stream.emit('error', err);
        this.coreLogger.warn(err);
      } else {
        this.coreLogger.error(err);
        // ignore next error event
        stream.on('error', () => {});
      }
      // ignore all data
      stream.resume();
    });

    return stream;
  }
}

然后在 controller 中使用

class SignUpController extends Controller {
  async submit() {
    const ctx = this.ctx;
    // 这样就算没有文件也不会报错,而且还能获得其它表单的值
    const stream = await ctx.getFileStreamWithoutFileNotFoundError();
    const { fileds } = stream;
   // ...
  }
}

@WeiGrand 我们来验证一下。

@WeiGrand https://github.com/eggjs/egg-multipart/pull/17 增加一个函数来更好解决此问题。

guide docs 也可以更新下

@chinalinbo tnpm update 后再看看,另外看你的 begg 对应的 egg 版本。 内部问题,在内网 issue 和钉群讨论。

ctx.getFileStream({ requireFile: false }): 文件非必须

Was this page helpful?
0 / 5 - 0 ratings

Related issues

atian25 picture atian25  ·  68Comments

Yao-JSON picture Yao-JSON  ·  34Comments

zlab picture zlab  ·  55Comments

itsky365 picture itsky365  ·  62Comments

XadillaX picture XadillaX  ·  44Comments