如何在 electron 中存取本地文件

最近在使用 Electron 做一个手写字体生成图片的工具。 不可避免的,遇到了通过Electron 往本地存文件的问题。

在Electron 中,存取本地文件,有很多种办法。本文介绍最常用的一种办法。 通过 Electron 框架提供的能力,和 Node.js 的 fs 文件管理模块实现本地文件的存取。

使用 app.getPath 和 fs 模块存储文件

首先,我们可以通过 app.getPath 来获取当前用户的 data 存放目录。

app.getPath('userData') 返回一个字符串,该字符串表示应用程序的用户数据目录的路径。这个目录通常是用来存储用户相关的数据,例如配置文件、缓存等。具体路径会根据操作系统而变化。

  • windows 系统中, 会返回类似(C:\Users\<username>\AppData\Roaming\<YourAppName>)这样的结果。

  • Mac 系统中,会返回(/Users/<username>/Library/Application Support/<YourAppName>) 这种结果。

  • linux 系统中 则会返回 (/home/<username>/.config/<YourAppName>) 这种结果。

我们通过 node.js 的path 模块, 使用 path.join(app.getPath('userData'), 'myFile.txt') 就能得到一个完整的文件路径。 接着使用 fs 模块的 来写入内容即可。

示例代码如下:

jsx 复制代码
const {
   app
} = require('electron');
const fs = require('fs');
const path = require('path');
const filePath = path.join(app.getPath('userData'),'/myfile.txt'); // Example file path
try {
   fs.writeFileSync(filePath, 'hello world', 'utf-8');
} catch (e) {
   console.error('Failed to save the file!');
}

这种做法,有一个问题。那就是,用户不能在保存的时候,主动选择文件存放目录。

使用 Dialog API 和 fs 模块配合

Electron 提供了一个 Dialog API 来处理文件对话框。您可以专门调用一个保存对话框,让用户选择保存文件的位置。

下面是一个简单的,示例代码:

jsx 复制代码
const {
   dialog
} = require('electron').remote;
const fs = require('fs');

const options = {
   title: 'Save file',
   defaultPath: 'my_filename',
   buttonLabel: 'Save',
   filters: [{
         name: 'txt',
         extensions: ['txt']
      },
      {
         name: 'All Files',
         extensions: ['*']
      },
   ],
};


dialog.showSaveDialog(null, options).then(({
   filePath
}) => {
   if (filePath) {
      fs.writeFileSync(filePath, 'hello world', 'utf-8');
   }
});

需要注意的点:

因为 fs 模块 和 Dialog 只能在,主进程中被调用。 但是我们的应用程序交互逻辑是在渲染进程,所以这段示例代码,只是演示了,如何去调用 Dialog 并手动选择文件存储位置。 并没有实际应用场景的参考意义。

实际应用场景封装 调整

对存取图片的封装想法跟之前的采集桌面思路基本一致(如果,没看过可以翻一下以前的文章)。 我们利用 Electron 的 ipcmain 模块在主进程中注册方法。 然后,在渲染进程去调用。 整理实现流程大概如下图所示。

实例代码:

jsx 复制代码
// 主进程 ->  main.js

// .... ohter code
// ... 在主进程注册我们封装后的 SaveFile 方法

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

/**

 * @param options: { title:  String, defaultPath: String, buttonLabel: String, filters: area}

 * @param content: String

 * @returns Promise

 */

ipcMain.handle('saveFile', async (event, content, options) => {
  let path;
  try {
  const { filePath } =  await dialog.showSaveDialog(null, options);
    path = filePath;
  } catch(e) {
    return Promise.reject({error: e, success: false});
  }

  if(path) {
    try {
      fs.writeFileSync(path, content, 'utf-8');
      return Promise.resolve({error: null, success: true});
    } catch(e) {
      return Promise.reject({error: e, success: false});
    }
  }
    return Promise.reject({error: e, success: false, canceled: true});
});


// 其他代码 ....
jsx 复制代码
// Vue 文件、

// 在我们,的Vue 业务中。直接通过 ipc 调用

<script setup>
  import {reactive, ref, onMounted} from "vue";
  const textContent = ref('hello world');
  const { ipcRenderer } = require('electron');

  const exportImg = async () => {
    try {
      // 注意->  第一个参数,为 主进程注册进来的方法名称。
      // 其他参数为, 主进程注册的函数参数。
      await ipcRenderer.invoke('saveFile', 'hello', {
        title: '导出图片',
        buttonLabel: '导出',
        defaultPath: 'text.txt',
        filters: [{
         name: 'All Files',
         extensions: ['*']
          }]
      })
    } catch(e) {
      console.error('出错了!', e)
    }
  }

</script>

<template>
  <a-config-provider :csp="{ nonce: 'YourNonceCode' }">
      <a-button type="primary" @click="exportImg">导出</a-button>
  </a-config-provider>
</template>

总结

在Electron 中,向本地存储和读取文件(文本或者是图片), 都离不开 node.js 的 fs 模块, 这个是文件系统处理的核心模块。

然后,我们搭配, path 模块。 dialog 模块,可以实现,用户主动选择存放位置。 或者 直接存到默认软件系统的 data 目录中。

当然,这些模块都只能在主进程中使用。 所以,我们不可避免的用到了, 之前的老朋友, ipc 进程间通讯方式。

本文,基于我在做一个手写照片 模拟器中遇到存储问题而来。 软件正在书写中。 后续会开源,并放出使用的视频。

请关注我的后续文章。

相关推荐
墨夏4 分钟前
TS 高级类型
前端·typescript
程序猿师兄13 分钟前
若依框架前端调用后台服务报跨域错误
前端
前端小巷子17 分钟前
跨标签页通信(三):Web Storage
前端·面试·浏览器
工呈士17 分钟前
TCP 三次握手与四次挥手详解
前端·后端·面试
BillKu18 分钟前
Vue3 + TypeScript + Element Plus + el-input 输入框列表按回车聚焦到下一行
前端·javascript·typescript
复苏季风19 分钟前
前端程序员unity学习笔记01: 从c#开始的入门,using命名空间,MonoBehaviour,static,public
前端
阿古达木21 分钟前
沉浸式改 bug,步步深入
前端·javascript·github
夏沫mds22 分钟前
Hyperledger Fabric食品溯源
运维·vue.js·go·vue·区块链·gin·fabric
stoneSkySpace30 分钟前
react 自定义状态管理库
前端·react.js·前端框架
堕落年代43 分钟前
SpringAI1.0的MCPServer自动暴露Tool
前端