You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Electron应用中如何递归列出选中文件夹内的所有文件?

Fixing Recursive Folder File Listing in Electron

Hey, I’ve dealt with this exact problem in Electron before! The issue is that while regular browsers return all nested files when using the webkitdirectory attribute, Electron’s Chromium implementation only gives you the top-level folders in the event.target.files list. To get all the files inside, we need to use Electron’s native file system capabilities instead of relying solely on the browser’s File API.

Why Your Current Code Isn’t Working

In standard browsers, the webkitdirectory attribute tells the browser to recursively collect all files in the selected folder(s) and return them in the files array. But in Electron, this behavior is restricted—you only get the top-level folders, not their contents. This is likely due to Electron’s security sandboxing and how it handles native file system access.

Solution 1: Direct File System Access in Renderer (Quick, Less Secure)

If you’re working on a development build or don’t mind relaxing some security settings temporarily, you can use Node.js’s fs module directly in the renderer process to traverse folders recursively.

Step 1: Update BrowserWindow Settings

First, enable Node.js integration in your main process (main.js):

const { app, BrowserWindow } = require('electron');
const path = require('path');

function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false // Required for renderer to access Node modules
    }
  });

  mainWindow.loadFile('index.html');
}

// Rest of your main process code...

Step 2: Modify Renderer Code (index.js)

Replace your existing listener with this recursive traversal code:

const fs = require('fs').promises;
const path = require('path');

document.getElementById("filepicker").addEventListener("change", async function(event) {
  const output = document.getElementById("listing");
  output.innerHTML = ''; // Clear previous results

  // Recursive function to traverse folders
  async function traverseFolder(dirPath, rootDir) {
    const entries = await fs.readdir(dirPath, { withFileTypes: true });
    for (const entry of entries) {
      const fullPath = path.join(dirPath, entry.name);
      if (entry.isDirectory()) {
        await traverseFolder(fullPath, rootDir); // Recurse into subfolders
      } else {
        // Calculate path relative to the selected root folder
        const relativePath = path.relative(rootDir, fullPath);
        const listItem = document.createElement("li");
        listItem.textContent = relativePath;
        output.appendChild(listItem);
      }
    }
  }

  // Process each selected folder
  for (const file of event.target.files) {
    if (!file.type) { // Check if it's a folder (Electron sets folder type to empty string)
      await traverseFolder(file.path, file.path);
    } else {
      // Handle single file selection
      const listItem = document.createElement("li");
      listItem.textContent = file.name;
      output.appendChild(listItem);
    }
  }
}, false);

Solution 2: IPC Communication (Secure, Production-Ready)

For production apps, it’s best practice to keep file system operations in the main process (to avoid exposing Node.js APIs to the renderer). We’ll use Electron’s IPC (Inter-Process Communication) to send folder paths from the renderer to the main process, which will handle the traversal and send back the results.

Step 1: Update Main Process (main.js)

Add an IPC handler to handle folder traversal requests:

const { app, BrowserWindow, ipcMain } = require('electron');
const fs = require('fs').promises;
const path = require('path');

function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      contextIsolation: true, // Keep security enabled
      preload: path.join(__dirname, 'preload.js') // Expose safe APIs to renderer
    }
  });

  mainWindow.loadFile('index.html');
}

// IPC handler to traverse folders
ipcMain.handle('traverse-folder', async (_, folderPath) => {
  const fileList = [];

  async function traverse(dir, rootDir) {
    const entries = await fs.readdir(dir, { withFileTypes: true });
    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);
      if (entry.isDirectory()) {
        await traverse(fullPath, rootDir);
      } else {
        fileList.push(path.relative(rootDir, fullPath));
      }
    }
  }

  await traverse(folderPath, folderPath);
  return fileList;
});

// Rest of your main process code...

Step 2: Create Preload Script (preload.js)

This script safely exposes the IPC method to the renderer without exposing full Node.js access:

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  traverseFolder: (folderPath) => ipcRenderer.invoke('traverse-folder', folderPath)
});

Step 3: Update Renderer Code (index.js)

Use the exposed API to request folder traversal from the main process:

document.getElementById("filepicker").addEventListener("change", async function(event) {
  const output = document.getElementById("listing");
  output.innerHTML = '';

  for (const file of event.target.files) {
    if (!file.type) {
      // Request main process to traverse the folder
      const fileList = await window.electronAPI.traverseFolder(file.path);
      // Add all files to the list
      fileList.forEach(relativePath => {
        const listItem = document.createElement("li");
        listItem.textContent = relativePath;
        output.appendChild(listItem);
      });
    } else {
      const listItem = document.createElement("li");
      listItem.textContent = file.name;
      output.appendChild(listItem);
    }
  }
}, false);

Key Notes

  • Always prefer the IPC approach for production apps—it follows Electron’s security best practices by keeping sensitive file system operations in the main process.
  • When checking if a File object is a folder in Electron, look for an empty type property (since folders don’t have a MIME type).

内容的提问来源于stack exchange,提问作者Akash Srinivasan

火山引擎 最新活动