使用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
相关产品推荐
相关产品推荐

