Node.js文件夹文件展示应用问题:文件变更无法同步至页面
Hey there! Let's tackle that real-time file update issue you're facing with your Node.js API. The core problem with your current code is that it only reads the folder structure once when the server starts (via that setTimeout call), and never refreshes the data afterward. That's why any new files or deletions don't show up in your HTML page. Let's fix this with two solid approaches, depending on your needs.
Why Your Current Code Isn't Updating
Let's quickly break down the key issues:
- You populate the
itemsarray once at startup, but never re-read the folders when changes happen. - The callback-based
fsfunctions andsetTimeoutcreate unreliable timing—you can't guarantee the folder read finishes before the first API request. - No error handling for cases like missing folders or read permissions.
- Using
for...into iterate over arrays can cause unexpected behavior (it iterates over object properties, not just array elements).
Approach 1: Re-read Folders on Every API Request
This is the simplest fix—every time your frontend calls /FileCount, we fresh-read the entire folder structure. This ensures you always get the latest state, with minimal code changes.
We'll use fs.promises and async/await to clean up the callback mess and make the code more reliable:
const fs = require('fs').promises; const express = require('express'); const cors = require('cors'); const path = require('path'); // For cross-platform path handling const app = express(); app.use(cors()); app.use(express.static(__dirname)); const baseDir = 'Invoices'; // Async function to read the full folder structure async function getUpdatedFolderStructure() { const folderItems = []; try { // Read the main Invoices directory const mainDirFiles = await fs.readdir(baseDir); for (const subFolderName of mainDirFiles) { const subFolderPath = path.join(baseDir, subFolderName); const stats = await fs.stat(subFolderPath); // Only process subdirectories (error, processed, unprocessed) if (stats.isDirectory()) { // Read files inside the subfolder const subFolderFiles = await fs.readdir(subFolderPath); // Map files to your desired format const fileRecords = subFolderFiles.map(file => ({ filename: file, filepath: path.join(subFolderPath, file) })); folderItems.push({ file: subFolderName, count: subFolderFiles.length, files: fileRecords }); } } return folderItems; } catch (err) { console.error('Failed to read folder structure:', err); throw err; // Let Express handle the error response } } // Update the API endpoint to fetch fresh data on every request app.get('/FileCount', async (req, res) => { try { const latestFolderData = await getUpdatedFolderStructure(); res.send(latestFolderData); } catch (err) { res.status(500).send({ error: 'Unable to retrieve folder data' }); } }); app.listen(4000, () => { console.log('Listening on port 4000'); });
Pros of This Approach:
- Dead simple to implement—no extra listening logic needed.
- Guaranteed fresh data on every request.
- Cleaner, more maintainable code with
async/awaitand proper error handling. - Uses
path.jointo avoid cross-platform path issues (e.g.,/vs\on Windows).
Best For:
Low-traffic scenarios where you don't mind the small overhead of re-reading folders on each request.
Approach 2: Watch for Folder Changes (Cached Data)
If your frontend makes frequent requests to /FileCount, re-reading folders every time can waste resources. Instead, we'll use Node's built-in fs.watch to monitor the folders for changes, and keep a cached copy of the folder structure that updates automatically.
const fs = require('fs').promises; const fsSync = require('fs'); const express = require('express'); const cors = require('cors'); const path = require('path'); const app = express(); app.use(cors()); app.use(express.static(__dirname)); const baseDir = 'Invoices'; let cachedFolderData = []; // Cache to store the latest folder structure // Function to refresh the cached folder data async function refreshCachedData() { try { const folderItems = []; const mainDirFiles = await fs.readdir(baseDir); for (const subFolderName of mainDirFiles) { const subFolderPath = path.join(baseDir, subFolderName); const stats = await fs.stat(subFolderPath); if (stats.isDirectory()) { const subFolderFiles = await fs.readdir(subFolderPath); const fileRecords = subFolderFiles.map(file => ({ filename: file, filepath: path.join(subFolderPath, file) })); folderItems.push({ file: subFolderName, count: subFolderFiles.length, files: fileRecords }); } } cachedFolderData = folderItems; console.log('Folder data refreshed'); } catch (err) { console.error('Failed to refresh folder data:', err); } } // Set up watchers for the main folder and its subfolders function setupFolderWatchers() { // Watch the main Invoices directory for new/removed subfolders fsSync.watch(baseDir, (eventType, filename) => { if (filename) { console.log(`Change detected in ${baseDir}: ${filename}`); refreshCachedData(); } }); // Watch each subfolder for file changes async function watchSubFolders() { const mainDirFiles = await fs.readdir(baseDir); for (const subFolderName of mainDirFiles) { const subFolderPath = path.join(baseDir, subFolderName); const stats = await fs.stat(subFolderPath); if (stats.isDirectory()) { fsSync.watch(subFolderPath, (eventType, file) => { console.log(`Change detected in ${subFolderPath}: ${file}`); refreshCachedData(); }); } } } watchSubFolders(); } // Initialize cached data and start server (async () => { await refreshCachedData(); setupFolderWatchers(); app.listen(4000, () => { console.log('Listening on port 4000'); }); })(); // API endpoint returns cached data app.get('/FileCount', (req, res) => { res.send(cachedFolderData); });
Pros of This Approach:
- Faster API responses since we return cached data instead of re-reading folders every time.
- Automatic updates whenever files or folders are added/removed.
Notes:
fs.watchbehavior can vary slightly across operating systems (e.g., multiple events for a single change). For more reliable cross-platform watching, you could use a library likechokidar(but that's an extra dependency).- We initialize the cache on server startup so the first request has valid data.
Best For:
High-traffic scenarios where you want to minimize disk I/O and keep API responses fast.
Final Tips for Your Frontend
To make the updates feel truly real-time, your HTML page can:
- Poll the
/FileCountendpoint at regular intervals (e.g., every 2-5 seconds) usingsetInterval. - Or use WebSockets to push updates from the server to the frontend when changes are detected (but that's an extra layer of complexity).
内容的提问来源于stack exchange,提问作者Arti

