electron 从特定文件夹中获取项目作为电子中的数组

vcudknz3  于 2023-05-15  发布在  Electron
关注(0)|答案(1)|浏览(146)

我想得到的项目返回作为数组的文件。我已经得到了一个字符串路径dialog.showOpenDialog({properties:['openDirectory']}。显然,我找不到一个合适的方法来从文件夹路径中获取项目。我尝试了shell.openPath('path'),但它打开了一个新的窗口到特定的文件夹。
然后我尝试从Node.js使用fs(文件系统)。
我使用了fs.opendir(args, { encoding: "utf8", bufferSize: 64 }, (err, dir) => {if(err) {console.log(err)}},但是返回了undefined。有什么提示吗?
main.js

ipcMain.handle("get/folder", () => dialog.showOpenDialog({properties:['openDirectory']}))
// get the folder path

ipcMain.on("get/item", (event, args) => fs.opendir(args, { encoding: "utf8", bufferSize: 64 }, (err, dir) => {
    if(err) { console.log(err)}
}))
// get the items from the folder path

preload.js

const { ipcRenderer, contextBridge } = require("electron")

const openFolder = {
    GetFolder: () => ipcRenderer.invoke("get/folder"),
    GetItem: (folder) => ipcRenderer.send("get/item", folder) 
}

contextBridge.exposeInMainWorld("api", openFolder)

App.svelte

async function send() {
    fileFolder = await api.GetFolder()
    path = fileFolder.filePaths[0]
        if (!fileFolder.canceled) {
            files =  await api.GetItem(path)
            console.log(files) // returns undefined
        }
}

注意:已经尝试了fs.readdir,但仍然是undefined

sshcrbum

sshcrbum1#

你需要使用Node的readdir函数:

  • 同步(回调)fs.readdir(path[, options], callback)
  • 异步(承诺)fsPromises.readdir(path[, options])

设置readdir选项{withFileTypes: true}将简化使用dirent.isDirectory()检查返回的数组。IE:如果一个“文件”实际上是一个目录,不要将它添加到文件列表中。
此外,由于文件夹对话框是在主进程中创建的,因此可以将返回值(“path”)直接传递到fs.readdir函数中,而不是在主进程和渲染进程之间来回发送。
下面是一个最小可重复的示例,显示了fs.readdir的使用是多么简单,而且通过使用简单的preload.js脚本,主进程和渲染进程之间的交互也是如此。
main.js(主进程)
注意Electron的对话框“path”result.filePaths[0]是如何直接用作nodeFsPromise.readdir函数中的参数的。IE:不需要在渲染进程和主进程之间来回发送。

// Import required electron modules
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronDialog = require('electron').dialog;
const electronIpcMain = require('electron').ipcMain;

// Import required Node modules
const nodeFsPromise = require('fs').promises;
const nodePath = require('path');

// Prevent garbage collection
let window;

function createWindow() {
    const window = new electronBrowserWindow({
        x: 0,
        y: 0,
        width: 800,
        height: 600,
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            sandbox: true,
            preload: nodePath.join(__dirname, 'preload.js')
        }
    });

    window.loadFile(nodePath.join(__dirname, 'index.html'))
        .then(() => { window.show(); });

    return window;
}

electronApp.on('ready', () => {
    window = createWindow();
});

electronApp.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        electronApp.quit();
    }
});

electronApp.on('activate', () => {
    if (electronBrowserWindow.getAllWindows().length === 0) {
        createWindow();
    }
});

// ---

electronIpcMain.handle('get-files', () => {
    return electronDialog.showOpenDialog(window, {properties: ['openDirectory']})
        .then((result) => {
            // Bail early if user cancels dialog
            if (result.canceled) {
                return {'status': 'canceled'}
            } else {
                return nodeFsPromise.readdir(result.filePaths[0], {withFileTypes: true})
                    .then((files) => {
                        let fileList = [];

                        files.forEach(file => {
                            if (file.isDirectory() === false) {
                                fileList.push(file.name);
                            }
                        })

                        return (fileList.length === 0) ? {'status': 'empty'} : fileList;
                    })
                    .catch((error) => {
                        console.error(error);
                        return {'status': 'error'}
                    })
            }
        })
})

preload.js(主进程)
让我们通过定义一个方法而不是两个方法来简化事情。

// Import the necessary Electron modules
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;

// Exposed protected methods in the render process
contextBridge.exposeInMainWorld(
    // Allowed 'ipcRenderer' methods
    'ipcRenderer', {
        // From render to main and back again
        getFiles: () => {
            return ipcRenderer.invoke('get-files');
        }
    }
);

以及如何使用它…

/**
 *
 * Main --> Render
 * ---------------
 * Main:    window.webContents.send('channel', data); // Data is optional.
 * Preload: apiName: (listener) => { ipcRenderer.on('channel', listener); }
 * Render:  window.ipcRenderer.apiName((event, data) => { methodName(data); });
 *
 * Main --> Render (Once)
 * ----------------------
 * Main:    window.webContents.send('channel', data); // Data is optional.
 * Preload: apiName: (listener) => { ipcRenderer.once('channel', listener); }
 * Render:  window.ipcRenderer.apiName((event, data) => { methodName(data); });
 *
 * Render --> Main
 * ---------------
 * Render:  window.ipcRenderer.apiName(data); // Data is optional.
 * Preload: apiName: (data) => { ipcRenderer.send('channel', data) }
 * Main:    electronIpcMain.on('channel', (event, data) => { methodName(data); });
 *
 * Render --> Main (Once)
 * ----------------------
 * Render:  window.ipcRenderer.apiName(data); // Data is optional.
 * Preload: apiName: (data) => { ipcRenderer.send('channel', data); }
 * Main:    electronIpcMain.once('channel', (event, data) => { methodName(data); });
 *
 * Render --> Main (Value) --> Render
 * ----------------------------------
 * Render:  window.ipcRenderer.apiName(data).then((result) => { methodName(result); });
 * Preload: apiName: (data) => { return ipcRenderer.invoke('channel', data); }
 * Main:    electronIpcMain.handle('channel', (event, data) => { return someMethod(data); });
 *
 * Render --> Main (Promise) --> Render
 * ------------------------------------
 * Render:  window.ipcRenderer.apiName(data).then((result) => { methodName(result); });
 * Preload: apiName: (data) => { return ipcRenderer.invoke('channel', data); }
 * Main:    electronIpcMain.handle('channel', async (event, data) => {
 *              return await myPromise(data).then((result) => { return result; })
 *          });
 *
 * Main:    function myPromise(data) { return new Promise((resolve, reject) => { ... }; )};
 *
 * --------------------------------------------------------------------------------------------
 *
 * Main --> Main
 * -------------
 * Main:    appEvents.emit('channel', data);
 *
 * Main:    appEvents.on('channel', (data) => { methodName(data); }
 *
 */

index.html(渲染过程)
由于Electron的dialog.showOpenDialog([browserWindow, ]options)已经返回了一个promise,我们可以直接使用.then方法,而不是使用async/await函数。
添加了一些用户反馈,给予您了解实现它有多简单。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Electron Test</title>
        <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
    </head>

    <body>
        <input type="button" id="openDialog" value="Open Folder Dialog">

        <hr>

        <div id="output"></div>
    </body>

    <script>
        let output = document.getElementById('output');

        document.getElementById('openDialog').addEventListener('click', () => {
            window.ipcRenderer.getFiles()
                .then((result) => {
                    if (result['status'] === 'error') {
                        output.innertext = 'Error: Unable to perform operation'
                    } else if (result['status'] === 'canceled') {
                        output.innerText = 'Dialog cancelled / closed';
                    } else if (result['status'] === 'empty') {
                        output.innerText = 'No files in selected directory';
                    } else {
                        listFiles(result);
                    }
                })
        })

        function listFiles(files) {
            let html = '<ul>';
            for (let file of files) { html += '<li>' + file + '</li>'; }
            output.innerHTML = html + '</ul>';
        }
    </script>
</html>

相关问题