如何将目录下JSON文件内容读取为对象数组?Node.js异步问题求助
解决Node.js/Express中异步读取目录下JSON文件的问题
你说得完全对,问题核心就是异步操作的执行顺序——fs.readdir是异步方法,它的回调还没跑完,你后面遍历fileNames的代码就已经执行了,这时候fileNames还是空数组,自然读不到文件。另外你的代码里还有几个小细节需要修正,我一步步给你讲清楚:
先梳理你代码里的几个小问题
files.forEach的用法错了:forEach不会返回新数组,而且你在循环里又fileNames.push(file),这会导致文件名重复添加。其实直接fileNames = files就可以拿到所有文件名。- 编码拼写错误:
'uft8'应该是'utf8',不然读取文件会报错。 - 文件路径问题:读取文件时要带上目录路径
${dataFolder}/${fileName},不然Node会在当前执行目录找文件,而不是你指定的parsed_json_data文件夹。
方案一:用回调嵌套(基础写法)
把读取文件的逻辑放到fs.readdir的回调里,确保拿到文件名后再执行读取操作:
const express = require('express') const app = express() const port = 3001 const fs = require('fs') const dataFolder = './parsed_json_data' let fileObjects = [] fs.readdir(dataFolder, (err, files) => { if (err) { return console.log('读取目录失败:', err) } // 过滤出JSON文件(可选,避免读取非JSON文件) const jsonFiles = files.filter(file => file.endsWith('.json')) let completedReads = 0 jsonFiles.forEach(fileName => { const filePath = `${dataFolder}/${fileName}` fs.readFile(filePath, 'utf8', (err, data) => { if (err) { console.log(`读取文件${fileName}失败:`, err) completedReads++ return } try { const jsonObj = JSON.parse(data) fileObjects.push(jsonObj) } catch (parseErr) { console.log(`解析${fileName}的JSON失败:`, parseErr) } completedReads++ // 所有文件都读取完成后,可以做后续操作 if (completedReads === jsonFiles.length) { console.log('所有JSON文件读取完成:', fileObjects) } }) }) }) app.get('/', (req, res) => res.send('Hello World!')) app.listen(port, () => console.log(`Example app listening on port ${port}!`))
方案二:用Promise + async/await(推荐,更清晰)
Node.js的fs模块有Promise版本(fs.promises),用async/await可以让异步代码看起来像同步代码,解决你之前没成功的问题:
const express = require('express') const app = express() const port = 3001 const fs = require('fs').promises // 用Promise版本的fs const path = require('path') // 用path模块处理路径更可靠 const dataFolder = './parsed_json_data' let fileObjects = [] // 封装一个读取并解析单个JSON文件的函数 async function readAndParseJson(filePath) { try { const data = await fs.readFile(filePath, 'utf8') return JSON.parse(data) } catch (err) { console.log(`处理文件${filePath}失败:`, err) return null // 出错时返回null,不影响整体 } } // 主逻辑用async函数 async function loadAllJsonFiles() { try { // 先读取目录下的所有文件 const files = await fs.readdir(dataFolder) // 过滤JSON文件,生成完整路径 const jsonFilePaths = files .filter(file => file.endsWith('.json')) .map(file => path.join(dataFolder, file)) // 用path.join避免路径拼接的问题 // 并行读取所有JSON文件(效率更高) const jsonObjects = await Promise.all(jsonFilePaths.map(readAndParseJson)) // 过滤掉null(出错的文件) fileObjects = jsonObjects.filter(obj => obj !== null) console.log('所有JSON文件加载完成:', fileObjects) } catch (err) { console.log('加载JSON文件失败:', err) } } // 调用加载函数 loadAllJsonFiles() app.get('/', (req, res) => res.send('Hello World!')) app.listen(port, () => console.log(`Example app listening on port ${port}!`))
这个方案的优势:
- 用
fs.promises替代回调,代码更简洁易读。 - 用
Promise.all并行读取所有文件,比逐个读取效率更高。 - 用
path.join处理路径,避免不同操作系统下的路径分隔符问题(比如Windows是\,Linux是/)。 - 加入了错误处理,单个文件出错不会导致整个流程崩溃。
额外提示
如果你的Express接口需要返回这些JSON数据,可以把加载逻辑放到路由里,或者确保在接口被调用前已经加载完成(比如用await loadAllJsonFiles()后再启动服务器)。
内容的提问来源于stack exchange,提问作者Kristoffer Tølbøll
相关产品推荐
相关产品推荐

