1、章节目标
文件系统,是electron业务开发非常重要的一个模块,相关的api、使用场景,都会更丰富一些。
本章节,我们会总结几个常用的业务场景,结合文件相关的api,一起来实现相关功能。
- 熟悉文件系统相关的api
- 熟悉文件系统常用的一些业务场景
- 通过业务场景,反向理解文件系统相关api
2、相关的api
相关的api,我们虽然上个章节已经介绍过,但是我们再次简单总结一下。
- fs,是我们的文件处理的api,可以读取/写入相关的具体业务内容
- dialog,系统提供的文件对话框,可以选择文件,保存文件,得到相关文件的路径
- path,一般而言,path是一种跨平台的的路径处理方式
- app.getPath,获取相关模块的文件路径
3、文件系统使用场景
场景1、应用配置管理
需求说明:在桌面端应用开发的过程中,有时候我们想让代码本地,存储一些配置文件,方便我们业务的开展。比如,我们想在本地记录我们的应用的版本信息,然后根据不同的版本,业务上我们可能有不同的处理方式。特别是,不同的版本对比,然后走加载新版本的代码的业务流程,加载了新的版本,然后记录,更新本地的最新版本号。
ipcMain.handle("config:load", async () => {
// path.join,把两个路径拼接到一起
// app.getPath,获取本地数据
const configPath = path.join(app.getPath("userData"), "config.json");
try {
// fs.readFile,读取文件内容
const data = await fs.readFile(configPath, "utf8");
return { success: true, config: JSON.parse(data) };
} catch (error) {
// 配置文件不存在时返回默认配置
return { success: true, config: { theme: "light", language: "zh-CN" } };
}
});
ipcMain.handle("config:save", async (_e, config: any) => {
const configPath = path.join(app.getPath("userData"), "config.json");
try {
// fs.writeFile,写入文件内容
await fs.writeFile(configPath, JSON.stringify(config, null, 2));
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
});
场景2、数据持久化存储
数据的存储和读取,永远是一个应用软件的核心能力。
对于前端,我们的localstorage的api,就是提供数据存储的能力的。
那对于桌面端应用,主进程的数据的持久化存储,应该存放到标准的目录结构,在真实的业务场景中,一般就是path.join(app.getPath("userData"), "data", `xxx.json`),这样的结构。
// 本地数据库文件
ipcMain.handle("data:save", async (_e, table: string, data: any[]) => {
// 构造文件路径,文件名为table的变量,存储到data目录中。
const dataPath = path.join(app.getPath("userData"), "data", `${table}.json`);
try {
// 确保目录存在
await fs.mkdir(path.dirname(dataPath), { recursive: true });
// 把真实的数据data,写入到dataPath中
await fs.writeFile(dataPath, JSON.stringify(data, null, 2));
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
});
ipcMain.handle("data:load", async (_e, table: string) => {
const dataPath = path.join(app.getPath("userData"), "data", `${table}.json`);
try {
// 读取文件,文件名为table
const data = await fs.readFile(dataPath, "utf8");
return { success: true, data: JSON.parse(data) };
} catch (error) {
return { success: false, error: error.message };
}
});
场景3、日志记录系统
有了本地存储,在某些业务中,也会有本地日志。比如,在软件运行报错、异常关闭等非常规的场景中,希望有日志记录在本地,就会使用到我们的文件系统。
在以下的代码案例中,实现了以天为单位记录日志。
new Date().toISOString().split("T")[0],是一个类似2025-10-04格式的字符串,logFile是使用path.join的方式,进行文件路径的拼接。
每次wrire操作,就是向日志文件中写入内容,其核心api就是:fs.appendFile(logFile, logEntry)。
ipcMain.handle("log:write", async (_e, level: string, message: string) => {
const logDir = path.join(app.getPath("userData"), "logs");
// 定义文件名称,以天为单位,其中new Date().toISOString().split("T")[0],是一个类似2025-10-04格式的字符串。
const logFile = path.join(
logDir,
`${new Date().toISOString().split("T")[0]}.log`
);
try {
// 确保文件目录存储
await fs.mkdir(logDir, { recursive: true });
const timestamp = new Date().toISOString();
const logEntry = `[${timestamp}] ${level.toUpperCase()}: ${message}\n`;
// fs.appendFile,表示将log内容,写入到logFile中。
await fs.appendFile(logFile, logEntry);
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
});
场景4、数据备份
数据备份,在一些特殊的业务场景中,是一个场景操作。
比如,在网盘中,我们经常的一个操作就是:数据备份。
数据备份,核心点就是,把某个目录的文件,原封不动的存储到另一个目录中。
以下代码中:copyDirectory函数实现了,文件copy操作,备份的文件就是app.getPath("userData")下的所有文件和文件夹。备份文件存储的目录为:path.join(app.getPath("documents"), "AppBackups")。
// 数据备份
ipcMain.handle("backup:create", async (_e, backupName?: string) => {
const backupDir = path.join(app.getPath("documents"), "AppBackups");
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
const backupName = backupName || `backup_${timestamp}`;
const backupPath = path.join(backupDir, backupName);
try {
await fs.mkdir(backupPath, { recursive: true });
// 复制用户数据
const userDataPath = app.getPath("userData");
await copyDirectory(userDataPath, backupPath);
return { success: true, backupPath };
} catch (error) {
return { success: false, error: error.message };
}
});
// 辅助函数:复制目录
async function copyDirectory(src: string, dest: string) {
await fs.mkdir(dest, { recursive: true });
const items = await fs.readdir(src, { withFileTypes: true });
for (const item of items) {
const srcPath = path.join(src, item.name);
const destPath = path.join(dest, item.name);
if (item.isDirectory()) {
await copyDirectory(srcPath, destPath);
} else {
await fs.copyFile(srcPath, destPath);
}
}
}
场景5、文件的导入和导出
数据的导入导出,是一个高频场景。
它主要包括两方面的诉求,一方面是把数据从主体应用中,写入到我们的本地文件里。另一方面,就是把本地的文件进行读取,然后把获取的数据,提供给我们的应用使用。
以下就是一个基本的数据的导入导出的功能。
// 数据导出(数据写入本地文件)
ipcMain.handle(
"export:data",
async (_e, data: any, format: "json" | "csv" = "json") => {
const { canceled, filePath } = await dialog.showSaveDialog({
defaultPath: `export_${new Date().toISOString().split("T")[0]}.${format}`,
filters: [
{ name: "JSON Files", extensions: ["json"] },
{ name: "CSV Files", extensions: ["csv"] },
],
});
if (canceled) return { success: false, error: "Export canceled" };
try {
let content: string;
if (format === "json") {
content = JSON.stringify(data, null, 2);
} else {
// CSV 转换逻辑
content = convertToCSV(data);
}
await fs.writeFile(filePath, content, "utf8");
return { success: true, filePath };
} catch (error) {
return { success: false, error: error.message };
}
}
);
// 数据导入(把文件中的数据读取并提供给应用使用)
ipcMain.handle("import:data", async () => {
const { canceled, filePaths } = await dialog.showOpenDialog({
properties: ["openFile"],
filters: [
{ name: "JSON Files", extensions: ["json"] },
{ name: "CSV Files", extensions: ["csv"] },
],
});
if (canceled || filePaths.length === 0)
return { success: false, error: "Import canceled" };
try {
const content = await fs.readFile(filePaths[0], "utf8");
const ext = path.extname(filePaths[0]).toLowerCase();
let data: any;
if (ext === ".json") {
data = JSON.parse(content);
} else if (ext === ".csv") {
data = parseCSV(content);
}
return { success: true, data };
} catch (error) {
return { success: false, error: error.message };
}
});
以上5个使用场景,就是electron文件处理相关api的应用。
其实从整个技能体系来说,以上的内容,准确的说是属于node的知识,electron和node的关系,更像是electron提供了一套基于node的主进程能力。
4、结语
技术学习是一场持续的旅程,我很荣幸能与你同行。关注这个专栏,让我们共同探索 Electron 的奥秘,在实践中成长,在交流中进步。期待与你一起,从基础概念到项目实战,一步步构建出优秀的桌面应用程序!
关于本栏目
本专栏的所有内容和代码,github地址:https://github.com/techLaoLi/electron-tutorial
关于作者,前大厂技术专家,现大前端技术负责人,公众号:老李说技术。
欢迎大家的关注。
1571

被折叠的 条评论
为什么被折叠?



