如何获取打开文件的目录路径?

How to get the path to the directory of the opened file?

我是肯扬鲍尔斯。 我有一些代码可以打开一个打开的文件对话框。它打开 .DSCProj(特定于我的项目),我将在打开的文件所在的目录中执行 运行 一些终端命令。 我不知道该怎么做。

preload.ts:

import { ipcRenderer, contextBridge } from "electron";
import { dialog } from '@electron/remote'

contextBridge.exposeInMainWorld("api", {
    showOpenFileDialog: () => dialog.showOpenDialogSync({
        properties: ["openFile"],
        filters: [
            {
                name: "DSC Projects",
                extensions: ["DSCProj"],
            },
        ],
    })
});

NewProject.ts:

declare var api: any;

function OpenProject(): void {
  const file = api.showOpenFileDialog();
  console.log("Done")
  if(file != null){
    localStorage.setItem('DirPath', file);
    location.href='./views/projectOpen.html'
  }
}



(() => {
  document.querySelector('#btn-open-project')?.addEventListener('click', () => {
    OpenProject();
  }),
  document.querySelector('#btn-new-project')?.addEventListener('click', () => {
    location.href='./views/projectNew.html'
  })
})()

如您在第 7 行所见,我将本地存储设置为文件路径。但是我需要将它设置为文件所在目录的路径。

虽然 @electron/remote 的使用很棒,但通过在主线程而不是渲染线程中实现某些 Electron 模块可能会更好地为您服务。这主要是为了安全,但作为第二个重要原因,它使您的代码保持分离。 IE:关注点分离。

与 vanilla Javascript 不同,node.js has a simple function path.parse(path).dir 可以轻松地从文件路径中删除文件名(和扩展名),而无需担心 OS(IE:目录分隔符)你正在使用。这也将在您的主线程中实现。在你的渲染线程中实现这样的东西需要使用 vanilla Javascript 做更多的工作才能 OS 证明。

最后,在下面的代码中,我将使用 preload.js 脚本,该脚本仅处理消息及其数据在主线程和渲染线程之间的移动。我不认为 preload.js 脚本中函数的具体实现是正确的方法(尽管其他人可能会争论)。

Note: I am not using typescript in the below code, but you should get the general idea.


让我们在 invoke 方法中使用频道名称 getPath

preload.js(主线程)

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

// White-listed channels.
const ipc = {
    'render': {
        // From render to main.
        'send': [],
        // From main to render.
        'receive': [],
        // From render to main and back again.
        'sendReceive': [
            'getPath'
        ]
    }
};

// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
    // Allowed 'ipcRenderer' methods.
    'ipcRender', {
        // From render to main.
        send: (channel, args) => {
            let validChannels = ipc.render.send;
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, args);
            }
        },
        // From main to render.
        receive: (channel, listener) => {
            let validChannels = ipc.render.receive;
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender`.
                ipcRenderer.on(channel, (event, ...args) => listener(...args));
            }
        },
        // From render to main and back again.
        invoke: (channel, args) => {
            let validChannels = ipc.render.sendReceive;
            if (validChannels.includes(channel)) {
                return ipcRenderer.invoke(channel, args);
            }
        }
    }
);

现在,在主线程中,让我们监听 getPath 频道上的调用。调用时,打开 dialog and upon the return of a path, process it with Node's path.parse(path).dir 函数以删除文件名(和扩展名)。最后,return修改后的路径。

main.js(主线程)

const electronBrowserWindow = require('electron').BrowserWindow;
const electronDialog = require('electron').dialog;
const electronIpcMain = require('electron').ipcMain;

const nodePath = require("path");

let window;

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

    window.loadFile('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();
    }
});

// -----

// Let's listen for a call on the 'getPath' channel
electronIpcMain.handle('getPath', async () => {
    // Dialog options.
    const options = {
        properties: ["openFile"],
        filters: [
            {
                name: "DSC Projects",
                extensions: ["DSCProj"],
            }
        ]
    }

    // When available, return the modified path back to the render thread via IPC
    return await openDialog(window, options)
        .then((result) => {
            // User cancelled the dialog
            if (result.canceled === true) { return; }

            // Modify and return the path
            let path = result.filePaths[0];
            let modifiedPath = nodePath.parse(path).dir; // Here's the magic.
            console.log(modifiedPath); // Testing

            return modifiedPath;
        })
})

// Create an open dialog
function openDialog(parentWindow, options) {
    return electronDialog.showOpenDialog(parentWindow, options)
        .then((result) => { if (result) { return result; } })
        .catch((error) => { console.error('Show open dialog error: ' + error); });
}

在这里您将大致了解如何使用 returned 结果。

index.html(渲染线程)

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Open Project Test</title>
    </head>

    <body>
        <input type="button" id="btn-open-project" value="Open Project">
    </body>

    <script>
        document.getElementById('btn-open-project').addEventListener('click', () => {
            openProject();
         });

        function openProject() {
            window.ipcRender.invoke('getPath')
                .then((path) => {
                    // As we are using "invoke" a response will be returned, even if undefined.
                    if (path === undefined) { return; } // When user cancels dialog.

                    console.log(path); // Testing.
                    // window.localStorage.setItem('DirPath', path);
                    // location.href='./views/projectOpen.html';
                });
        }
    </script>
</html>