【multer】文件上传模块使用【stream】流的方式来读取数据

实际需求

在实际的项目开发过程中,文件上传有时候,我们只需要读取文件的内容,而不需要保存到本地。因为可能生产环境的服务器并不归开发人员管,你可能连【写】的权限都没有。所以读取文件这种操作,一般是,文件上传 -> 保存到buffer中 -> 用streamBuffer来接收数据。今天要讲的是,node.js中文件上传的一个模块【multer】使用流的方式来读取数据。

前置知识

我们需要用到 multer中的MemoryStorage,它可以将文件作为一个buffer保存到内存中;然后我们用node中的stream来接收这段buffer,并解析字节。

实现步骤

  1. 在express的route文件中,导入multer,并将storage设置成memoryStorage
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
  1. 在对应的上传url中,添加文件上传的请求头拦截

      app.route('/api/test/uploadFile').post(upload.single("file"),  TestController.uploadFile);
    
    

    这里的upload.single("file")的作用就是将上传的文件以buffer的形式保存到请求参数里面的file属性中。

    在这里需要补充一点,如果你的上传需要做权限校验,那权限校验的方法应该写在upload.single("file")后面。由于前端的文件上传的请求头content-typemultipart/form-data,而一般的数据传输的请求头的content-type是application/json。如果先去校验权限的话,会出现无法解析到session数据,无法获取请求头或者request中的数据;如果先调用权限校验方法,则文件上传的数据会因为请求转发而丢失。所以这时候最恰当的做法是,先接收文件的二进制数据,再设置请求头从而获取到request中的数据进行权限校验。

  2. 在TestController中用流将数据从字节转换成具体的字符,实现文件读取。

    const uploadFile = async (req, res) => {
        // 先检查文件的buffer是否存在,文件数据有没有保存到Buffer中
        if (_.isEmpty(req.file)) {
            return res.status(400).send({ result: 'error', message: 'file is empty.' });
      	}
        let fileData = [];	// 定义一个数据,用于保存文件的内容
        let len = 0;		// 保存流的长度
        const bufferStream = new stream.PassThrough(); // 声明一个PassThrough流,将输入字节传到输出
        bufferStream.end(req.file.buffer);	// 初始化的流,一定为空,这时候让它读取文件的流
        // 当有数据,就写入到数组中
        bufferStream.on('data', (chunk) => {
          fileData.push(chunk);
          len += chunk.length;
        });
        // 这个流到尽头了
        bufferStream.on('end', async () => {
            // 将文件流的内容合并起来,并从字节转成String字符
            let content = Buffer.concat(fileData, len).toString();
            // 下面做一些具体的读取文件业务操作,你可以调用你的service层,这样会比较合理
            ......
            // 你需要在这里将响应结果返回,写在外面是没用的
            return res.json({result: 'SUCCESS'});
        }
        // 这个只有在流的读写操作错误的时候才会触发
        bufferStream.on('error', (error) => {
            console.log(error);
        }
    }
    

    看到这里你可能会觉得很疑惑,为什么我要把流写在这里,不写在service层,还要在流里面将结果返回?因为这个流的事件监听是异步的,这个异步你完全不知道它什么时候返回,更没有用Promise或者await去等待它的结果。事件监听没有回调函数,如果你需要 读取数据 -> 处理数据 -> 返回处理后的数据,也就是你要返回数据给前端,那你就只能将流和事件监听写在Controller这里。

    如果不需要什么返回值,我只要你读取数据,然后给我保存就行了,我Controller不等这流的处理结果是什么,先随便响应点什么给前端意思意思就行了。那写在service层就完美了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值