记egg接受前端文件,post传给后端
发布于 6 年前 作者 Helovebai 5209 次浏览 来自 分享

前端采用post请求,在ctx.body中接收不到前端传回来的数据。 解决方案1 使用multiparty插件,在form.parse中可以接受到前端用form表单传回来的参数以及文件。 遇到的问题如下: 使用multiparty接受到的文件,但是文件为file格式,并且经过编码,这又是一大坑,目前还没解决,如果有解决办法欢迎各位大神留言。 解决方案二 使用egg自带方法ctx.getFileStream获取前端传回来的数据,当part.length存在时处理前端传来的form参数,当part.length 不存在时,说明是文件,处理前端传回来的文件。 遇到的问题如下: 由于解决方案一不可用,木办法,只能采用方案二,但是还有坑啊~!使用egg自带方法收到的part为filestream,node中的流有Writable,Readable,Duplex,Transform四种,如果把filestream append到form-date中,后端那边会接受不到参数。 个人思路是先转换为buffer,这里使用stream-to-array插件,完了用fs.createReadStream去读取buffer,但是使用过后发现报错。上网查后发现那个方法的本质是调用open()方法。原文所以又采用stream.PassThrough()方法将buffer转换为流,但是转换出来的流的格式为througStream,还不是我想要的流。头大,同样,这个问题也没解决,所以就留给各位大神了。 最终方案: 使用方案二接受前端文件,使用pipe管道将filestream写到本地,之后使用fs.createreadstream方法去读取本地文件,通过axios发送请求,success。 主要代码如下:

	while ((part = await parts()) != null) {
      if (part.length) {
	  // 接受前端传来的字段,代码有些生硬...  尴尬   =,=   ...  
        switch (part[0]) {
          case 'token' : token = part[1]; break;
          ...
        }
      } else {
        if (!part.filename) {
          return;
        }
        const tempPath = path.resolve(`./app/public/resource${Math.random()}${part.filename}`);
		// 使用管道将filestream写入本地
        part.pipe(fs.createWriteStream(tempPath));
		// 使用promise 判断文件写入完成。
        const fsEnd = async part => {
          return new Promise(resolve => {
            part.on('end', function() {
              resolve();
            });
          });
        };
        await fsEnd(part);
		// 由于node并没有form数据格式,所以要用form-data插件。
        form.append('file', fs.createReadStream(tempPath));
        form.append('module', filePath);
        let result;
		result = await axios.post('http://**********', form, {
            headers: form.getHeaders(),
          });
         // 上传完毕之后,删除本地文件。
          fs.unlink(tempPath, () => {
            console.log('文件删除成功');
          });
      }
    }
7 回复

注:由于目前服务并没有上线,所以不确定会有什么问题,如果出现问题我会及时留言~

如果文件不是特别大的话 可以尝试让前端传base64过来 然后接受到base64转成buffer

@Helovebai 已成功上线,目前未发现问题。

@soeyi 如果用buffer的话,用fs.createreadstream去读会报错,没法传给后端

@Helovebai 前端用fileReader 就可以获取到base64 后端只要把base64 转成buffer 就好了 不需要createReadStream

原因: ctx.getFileStream() 只能处理通过表单上传的文件(流), 而转发过来的 ReadStream 是没有 Content-Type 之类键名,所以 ctx.getFileStream() 调用到的 multipart() 方法检查到 Content-Type 值非 Content-Type must be multipart/* 就会抛出异常。

解决方案https://github.com/waitingsong/upload-demo

  • 直接发送数据流,而非采用 FormData 构造表单文件域
  • 表单域(文件名) 通过请求 header 来传送
  • 在 saveFile 接口中直接读取数据流,而非使用 ctx.getFileStream() 来处理

不清楚 request 模块如何发送原始数据流,所以代码用的自己的轮子。可自行尝试 request

回到顶部