You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Buffer调用file-type npm模块却返回null,问题出在哪?

问题分析与解决方案

你遇到的核心问题有两个:一是错误地从req对象获取文件数据,导致file-type拿到的不是真实的文件Buffer;二是MIME类型判断的逻辑错误,会导致所有合法视频类型都被误判。

1. 为什么file-type返回null?

在GridFsStorage的file回调里,你监听了req的data事件来收集Buffer,但这里的req已经被multer处理过了——上传的文件内容并不在req的流中,而是通过file.stream提供的文件流来传递。你现在收集到的buffer其实是空的或者是请求的其他无关数据,自然无法被file-type识别,所以返回null。

2. MIME类型判断的逻辑错误

你的条件:

if (fType.mime !== 'video/mp4' || fType.mime !== 'video/webm' || fType.mime !== 'video/ogg') {
  return reject(new Error('Unsupported file format'));
}

这个逻辑永远为true——比如当文件是video/mp4时,它不等于video/webm,所以条件成立,直接触发错误。正确的逻辑应该是判断当前MIME类型不在允许的列表里,用数组的includes方法来反向判断会更清晰准确。

修正后的代码

下面是修复后的完整路由代码,我标注了关键修改点:

'use strict';
const router = require('express').Router();
const VideoInfo = require('../../models/VideoInfo');
const VideoAmount = require('../../models/VideoAmount');
const path = require('path');
const Lib = require('../../lib/Lib');
const multer = require('multer');
const GridFsStorage = require('multer-gridfs-storage');
const fileType = require('file-type');
const { pipeline } = require('stream/promises');
const { Readable } = require('stream');

// Defines storage of files with validation
const storage = new GridFsStorage({
  url: process.env.dbURL,
  file: async (req, file) => { // 改为async函数,方便处理异步流操作
    // 提前验证用户权限,避免无效操作
    if (!req.session.username) {
      throw new Error('Unauthorized file upload attempt');
    }

    // 读取文件流的前4100字节(file-type推荐的最小识别字节数)
    const fileChunk = [];
    const maxChunkSize = 4100;
    await pipeline(
      file.stream,
      new Readable({
        read(size) {
          const chunk = file.stream.read(size);
          if (chunk) {
            if (Buffer.concat(fileChunk).length < maxChunkSize) {
              fileChunk.push(chunk);
            }
            this.push(chunk);
          } else {
            this.push(null);
          }
        }
      })
    ).catch(err => { throw err; });

    const buffer = Buffer.concat(fileChunk);
    const fType = fileType(buffer);

    // 验证文件类型
    if (!fType) {
      throw new Error('Unsupported file format');
    }

    const allowedMimeTypes = ['video/mp4', 'video/webm', 'video/ogg'];
    if (!allowedMimeTypes.includes(fType.mime)) {
      throw new Error('Unsupported file format');
    }

    // 修改文件名并返回文件信息
    const fileName = Lib.make.randomString() + path.extname(file.originalname);
    return {
      filename: fileName,
      bucketName: 'uploads'
    };
  }
});

const upload = multer({ storage });

router.route('/upload')
  .get((req, res) => {
    // renders upload form, not really relevant
  })
  .post(upload.single('video'), async (req, res) => {
    // file gets saved to DB with upload.single-function
  });

module.exports = router;

关键修改说明:

  • 将file回调改为async函数,便于处理异步流操作
  • 提前验证用户权限,避免不必要的文件读取操作
  • 通过file.stream获取真实的文件流,读取足够识别类型的字节数来生成Buffer
  • 使用数组includes方法判断MIME类型,逻辑清晰且正确
  • 使用stream/promises的pipeline安全处理流操作,避免内存泄漏

另外补充:相比依赖上传时的file.mimetype(可被伪造),用file-type直接验证文件内容的方式更可靠,能有效防止恶意文件上传。

内容的提问来源于stack exchange,提问作者Edvin Larsson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:44:54