Electron 智能文件分析器开发实战适配鸿蒙
Electron 智能文件分析器开发实战适配鸿蒙
目录
- 功能概述
- 技术架构
- 文件编码检测
- 文本文件分析
- 图片文件分析
- UI 设计与交互
- 完整代码实现
- 功能扩展
- 性能优化
- 最佳实践
- 常见问题
功能概述
智能文件分析器是一个强大的文件信息提取工具,能够自动识别文件类型并提取详细的统计信息。该功能在文件选择的基础上,进一步提供了深度的文件分析能力。
核心功能
-
文件类型识别
- 根据扩展名显示对应的文件图标
- 支持 30+ 种常见文件类型
-
文本文件分析
- 编码格式检测(UTF-8、ASCII、UTF-16 等)
- 行数统计
- 单词数统计
- 字符数统计(含/不含空格)
- 段落数统计
- 内容预览(前 20 行)
-
图片文件分析
- 图片格式识别(PNG、JPEG、GIF)
- 图片尺寸提取(宽度、高度)
- 颜色深度检测(PNG)
- 图片预览
-
文件统计信息
- 创建时间
- 访问时间
- 文件扩展名
应用场景
- 📝 代码审查:快速了解代码文件的行数和结构
- 📊 文档分析:统计文档的字数和段落数
- 🖼️ 图片管理:查看图片尺寸和格式信息
- 🔍 文件检查:检测文件编码和基本信息
技术架构
分析流程
用户选择文件↓
点击"智能分析文件"按钮↓
读取文件 Buffer↓
检测文件编码↓
根据文件类型分类处理├── 文本文件 → 文本分析├── 图片文件 → 图片分析└── 其他文件 → 基础信息↓
生成统计结果↓
显示分析结果和预览
关键技术
- Buffer 操作:使用 Node.js Buffer 读取文件二进制数据
- 文件头解析:通过文件头(Magic Number)识别文件格式
- 编码检测:通过 BOM 和字符分析检测文件编码
- 正则表达式:用于文本统计(单词、段落等)
- 位运算:用于解析图片文件的尺寸信息
文件编码检测
BOM(Byte Order Mark)检测
BOM 是文件开头的特殊字节序列,用于标识文件编码:
function detectEncoding(buffer) {// UTF-8 BOM: EF BB BFif (buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF) {return 'UTF-8 (BOM)';}// UTF-16 LE BOM: FF FEif (buffer[0] === 0xFF && buffer[1] === 0xFE) {return 'UTF-16 LE';}// UTF-16 BE BOM: FE FFif (buffer[0] === 0xFE && buffer[1] === 0xFF) {return 'UTF-16 BE';}// 无 BOM,通过字符分析return detectEncodingByContent(buffer);
}
基于内容的编码检测
当文件没有 BOM 时,通过检查字符范围来判断编码:
function detectEncodingByContent(buffer) {let hasNonAscii = false;// 只检查前 1000 字节,提高性能for (let i = 0; i < Math.min(buffer.length, 1000); i++) {if (buffer[i] > 127) {hasNonAscii = true;break;}}return hasNonAscii ? 'UTF-8 (可能)' : 'ASCII';
}
编码检测的局限性
- 简单检测:只能识别常见的编码格式
- 准确度:对于没有 BOM 的文件,准确度有限
- 改进方案:可以使用
chardet或jschardet库提高准确度
// 使用 chardet 库(需要安装:npm install chardet)
const chardet = require('chardet');function detectEncodingAdvanced(buffer) {const detected = chardet.detect(buffer);return detected || '未知编码';
}
文本文件分析
行数统计
function countLines(content) {// 使用换行符分割const lines = content.split('\n');return lines.length;
}
注意:
- Windows 使用
\r\n,Unix/Linux 使用\n split('\n')可以处理两种情况
单词数统计
function countWords(content) {// 使用正则表达式分割单词// \s+ 匹配一个或多个空白字符const words = content.split(/\s+/).filter(w => w.length > 0);return words.length;
}
正则表达式说明:
/\s+/:匹配一个或多个空白字符(空格、制表符、换行等)filter(w => w.length > 0):过滤空字符串
字符数统计
function countCharacters(content) {return {withSpaces: content.length, // 含空格withoutSpaces: content.replace(/\s/g, '').length // 不含空格};
}
段落数统计
function countParagraphs(content) {// 段落由空行分隔(\n\n 或 \n\s*\n)const paragraphs = content.split(/\n\s*\n/).filter(p => p.trim().length > 0);return paragraphs.length;
}
完整的文本分析函数
function analyzeTextFile(content, encoding) {const lines = content.split('\n');const words = content.split(/\s+/).filter(w => w.length > 0);const chars = content.length;const charsNoSpaces = content.replace(/\s/g, '').length;const paragraphs = content.split(/\n\s*\n/).filter(p => p.trim().length > 0).length;return {type: 'text',encoding: encoding,lines: lines.length,words: words.length,characters: chars,charactersNoSpaces: charsNoSpaces,paragraphs: paragraphs};
}
文本预览
function getTextPreview(content, maxLines = 20) {const lines = content.split('\n');const previewLines = lines.slice(0, maxLines);const preview = previewLines.join('\n');if (lines.length > maxLines) {return preview + '\n\n... (还有 ' + (lines.length - maxLines) + ' 行)';}return preview;
}
图片文件分析
文件头(Magic Number)识别
不同格式的图片文件有独特的文件头:
| 格式 | 文件头(十六进制) | 文件头(ASCII) |
|---|---|---|
| PNG | 89 50 4E 47 | .PNG |
| JPEG | FF D8 FF | ÿØÿ |
| GIF | 47 49 46 38 | GIF8 |
| BMP | 42 4D | BM |
PNG 文件解析
PNG 文件的结构:
- 文件头:8 字节(89 50 4E 47 0D 0A 1A 0A)
- IHDR 块:包含图片尺寸信息(从第 16 字节开始)
function parsePNG(buffer) {// 检查 PNG 文件头if (buffer[0] !== 0x89 || buffer[1] !== 0x50 || buffer[2] !== 0x4E || buffer[3] !== 0x47) {return null;}// PNG 尺寸信息在 IHDR 块中(偏移 16-23)// 宽度:4 字节,大端序const width = (buffer[16] << 24) | (buffer[17] << 16) | (buffer[18] << 8) | buffer[19];// 高度:4 字节,大端序const height = (buffer[20] << 24) | (buffer[21] << 16) | (buffer[22] << 8) | buffer[23];// 颜色深度:1 字节(第 24 字节)const colorDepth = buffer[24] * 8;return {format: 'PNG',width: width,height: height,colorDepth: colorDepth};
}
位运算说明:
<<:左移运算符|:按位或运算符- 大端序(Big-Endian):高位字节在前
GIF 文件解析
GIF 文件的结构:
- 文件头:6 字节(GIF87a 或 GIF89a)
- 逻辑屏幕描述符:7 字节(包含尺寸信息)
function parseGIF(buffer) {// 检查 GIF 文件头if (buffer[0] !== 0x47 || buffer[1] !== 0x49 || buffer[2] !== 0x46 || buffer[3] !== 0x38) {return null;}// GIF 尺寸信息在小端序(Little-Endian)// 宽度:2 字节(偏移 6-7)const width = (buffer[7] << 8) | buffer[6];// 高度:2 字节(偏移 8-9)const height = (buffer[9] << 8) | buffer[8];return {format: 'GIF',width: width,height: height};
}
JPEG 文件解析
JPEG 文件解析较复杂,因为尺寸信息在 SOF(Start of Frame)段中,位置不固定:
function parseJPEG(buffer) {// 检查 JPEG 文件头if (buffer[0] !== 0xFF || buffer[1] !== 0xD8) {return null;}// JPEG 尺寸信息需要查找 SOF 段// 这里简化处理,实际需要遍历文件查找 SOF 标记let i = 2;while (i < buffer.length - 1) {if (buffer[i] === 0xFF && (buffer[i + 1] >= 0xC0 && buffer[i + 1] <= 0xC3)) {// 找到 SOF 段const height = (buffer[i + 5] << 8) | buffer[i + 6];const width = (buffer[i + 7] << 8) | buffer[i + 8];return {format: 'JPEG',width: width,height: height};}i++;}return {format: 'JPEG',width: '需完整解析',height: '需完整解析'};
}
统一的图片分析函数
function analyzeImageFile(buffer) {// PNGif (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4E && buffer[3] === 0x47) {return parsePNG(buffer);}// JPEGif (buffer[0] === 0xFF && buffer[1] === 0xD8) {return parseJPEG(buffer);}// GIFif (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x38) {return parseGIF(buffer);}return null;
}
UI 设计与交互
HTML 结构
<div class="file-analyzer" id="file-analyzer"><button id="analyze-file-btn"><span id="analyze-btn-text">🔍 智能分析文件</span><span id="analyze-loading" class="loading"></span></button><div class="analyzer-result" id="analyzer-result"><div class="file-icon" id="file-icon"></div><h4>📊 文件分析结果</h4><div id="analyzer-stats"></div><div id="file-preview-container"><h4>👀 内容预览</h4><div class="file-preview" id="file-preview"></div></div></div>
</div>
CSS 样式
.file-analyzer {margin-top: 20px;padding: 20px;background: rgba(255, 255, 255, 0.15);border-radius: 10px;
}.file-analyzer button {background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);color: white;border: none;padding: 12px 30px;font-size: 1em;border-radius: 25px;cursor: pointer;transition: all 0.3s ease;box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}.file-analyzer button:hover {transform: translateY(-2px);box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
}.analyzer-result .stat-item {margin: 10px 0;padding: 8px;background: rgba(0, 0, 0, 0.2);border-radius: 5px;display: flex;justify-content: space-between;align-items: center;
}.file-preview {max-height: 200px;overflow-y: auto;font-family: 'Monaco', 'Courier New', monospace;font-size: 0.85em;line-height: 1.6;
}
加载动画
.loading {display: inline-block;width: 20px;height: 20px;border: 3px solid rgba(255, 255, 255, 0.3);border-radius: 50%;border-top-color: #fff;animation: spin 1s ease-in-out infinite;
}@keyframes spin {to { transform: rotate(360deg); }
}
交互逻辑
analyzeBtn.addEventListener('click', async () => {if (!currentFilePath) {alert('请先选择一个文件');return;}// 禁用按钮,显示加载状态analyzeBtn.disabled = true;analyzeBtnText.textContent = '分析中';analyzeLoading.style.display = 'inline-block';try {await analyzeFile(currentFilePath);} catch (error) {console.error('分析出错:', error);alert('分析文件时出错: ' + error.message);} finally {// 恢复按钮状态analyzeBtn.disabled = false;analyzeBtnText.textContent = '🔍 智能分析文件';analyzeLoading.style.display = 'none';}
});
完整代码实现
文件类型图标映射
function getFileIcon(ext) {const iconMap = {'.txt': '📄', '.md': '📝', '.json': '📋', '.js': '📜', '.html': '🌐', '.css': '🎨','.jpg': '🖼️', '.jpeg': '🖼️', '.png': '🖼️', '.gif': '🖼️', '.mp4': '🎬', '.avi': '🎬','.pdf': '📕', '.doc': '📘', '.docx': '📘','.xls': '📊', '.xlsx': '📊', '.zip': '📦','.py': '🐍', '.java': '☕', '.cpp': '⚡','.go': '🐹', '.rs': '🦀', '.php': '🐘'};return iconMap[ext.toLowerCase()] || '📁';
}
主分析函数
async function analyzeFile(filePath) {try {const stats = fs.statSync(filePath);const ext = path.extname(filePath).toLowerCase();const buffer = fs.readFileSync(filePath);const encoding = detectEncoding(buffer);// 显示文件图标fileIcon.textContent = getFileIcon(ext);const statsHtml = [];// 基础信息statsHtml.push(`<div class="stat-item"><span class="stat-label">文件扩展名</span><span class="stat-value">${ext || '无'}</span></div><div class="stat-item"><span class="stat-label">创建时间</span><span class="stat-value">${formatDate(stats.birthtime)}</span></div><div class="stat-item"><span class="stat-label">访问时间</span><span class="stat-value">${formatDate(stats.atime)}</span></div>`);// 文本文件分析const textExtensions = ['.txt', '.md', '.json', '.js', '.html', '.css', '.py', '.java', '.cpp', '.c', '.go', '.rs', '.php', '.xml', '.yaml', '.yml'];if (textExtensions.includes(ext)) {try {const content = buffer.toString('utf-8');const analysis = analyzeTextFile(content, encoding);statsHtml.push(`<div class="stat-item" style="background: rgba(255, 215, 0, 0.2); margin-top: 15px;"><span class="stat-label">📝 文本分析</span><span class="stat-value"></span></div><div class="stat-item"><span class="stat-label">编码格式</span><span class="stat-value">${analysis.encoding}</span></div><div class="stat-item"><span class="stat-label">总行数</span><span class="stat-value">${analysis.lines.toLocaleString()}</span></div><div class="stat-item"><span class="stat-label">单词数</span><span class="stat-value">${analysis.words.toLocaleString()}</span></div><div class="stat-item"><span class="stat-label">字符数(含空格)</span><span class="stat-value">${analysis.characters.toLocaleString()}</span></div><div class="stat-item"><span class="stat-label">字符数(不含空格)</span><span class="stat-value">${analysis.charactersNoSpaces.toLocaleString()}</span></div><div class="stat-item"><span class="stat-label">段落数</span><span class="stat-value">${analysis.paragraphs}</span></div>`);// 显示预览const previewLines = content.split('\n').slice(0, 20);filePreview.textContent = previewLines.join('\n');if (previewLines.length < analysis.lines) {filePreview.textContent += '\n\n... (还有 ' + (analysis.lines - previewLines.length) + ' 行)';}filePreviewContainer.style.display = 'block';} catch (e) {statsHtml.push(`<div class="stat-item"><span class="stat-label">⚠️ 无法读取文本内容</span><span class="stat-value">${e.message}</span></div>`);}}// 图片文件分析if (['.jpg', '.jpeg', '.png', '.gif', '.bmp'].includes(ext)) {const imageAnalysis = analyzeImageFile(buffer);if (imageAnalysis) {statsHtml.push(`<div class="stat-item" style="background: rgba(255, 215, 0, 0.2); margin-top: 15px;"><span class="stat-label">🖼️ 图片分析</span><span class="stat-value"></span></div><div class="stat-item"><span class="stat-label">图片格式</span><span class="stat-value">${imageAnalysis.format}</span></div><div class="stat-item"><span class="stat-label">宽度</span><span class="stat-value">${imageAnalysis.width} px</span></div><div class="stat-item"><span class="stat-label">高度</span><span class="stat-value">${imageAnalysis.height} px</span></div>`);// 显示图片预览const img = document.createElement('img');img.src = `file://${filePath}`;img.style.maxWidth = '100%';img.style.maxHeight = '200px';img.style.borderRadius = '8px';filePreview.innerHTML = '';filePreview.appendChild(img);filePreviewContainer.style.display = 'block';}}analyzerStats.innerHTML = statsHtml.join('');analyzerResult.classList.add('show');} catch (error) {console.error('分析文件失败:', error);analyzerStats.innerHTML = `<div class="stat-item" style="background: rgba(255, 0, 0, 0.2);"><span class="stat-label">❌ 分析失败</span><span class="stat-value">${error.message}</span></div>`;analyzerResult.classList.add('show');}
}
功能扩展
1. 代码文件分析
function analyzeCodeFile(content, ext) {const analysis = analyzeTextFile(content);// 代码特定统计const codeStats = {...analysis,functions: countFunctions(content, ext),classes: countClasses(content, ext),comments: countComments(content, ext),imports: countImports(content, ext)};return codeStats;
}function countFunctions(content, ext) {// JavaScript/TypeScriptif (['.js', '.ts', '.jsx', '.tsx'].includes(ext)) {const matches = content.match(/function\s+\w+|const\s+\w+\s*=\s*\(/g);return matches ? matches.length : 0;}// Pythonif (ext === '.py') {const matches = content.match(/def\s+\w+/g);return matches ? matches.length : 0;}return 0;
}function countComments(content, ext) {let pattern;if (['.js', '.ts', '.jsx', '.tsx', '.java', '.cpp', '.c'].includes(ext)) {pattern = /\/\/.*|\/\*[\s\S]*?\*\//g;} else if (ext === '.py') {pattern = /#.*/g;} else {return 0;}const matches = content.match(pattern);return matches ? matches.length : 0;
}
2. JSON 文件验证
function analyzeJSONFile(content) {try {const json = JSON.parse(content);return {valid: true,type: Array.isArray(json) ? '数组' : '对象',keys: Object.keys(json).length,size: JSON.stringify(json).length};} catch (error) {return {valid: false,error: error.message};}
}
3. Markdown 文件分析
function analyzeMarkdownFile(content) {const analysis = analyzeTextFile(content);return {...analysis,headings: (content.match(/^#+\s/gm) || []).length,links: (content.match(/\[.*?\]\(.*?\)/g) || []).length,images: (content.match(/!\[.*?\]\(.*?\)/g) || []).length,codeBlocks: (content.match(/```[\s\S]*?```/g) || []).length};
}
4. 文件哈希计算
const crypto = require('crypto');function calculateFileHash(filePath, algorithm = 'md5') {const buffer = fs.readFileSync(filePath);const hash = crypto.createHash(algorithm);hash.update(buffer);return hash.digest('hex');
}// 使用
const md5Hash = calculateFileHash(filePath, 'md5');
const sha256Hash = calculateFileHash(filePath, 'sha256');
5. 文件依赖分析
function analyzeDependencies(content, ext) {const dependencies = [];// JavaScript/TypeScriptif (['.js', '.ts', '.jsx', '.tsx'].includes(ext)) {const importMatches = content.match(/import\s+.*?\s+from\s+['"](.*?)['"]/g);if (importMatches) {importMatches.forEach(match => {const dep = match.match(/['"](.*?)['"]/)[1];dependencies.push(dep);});}}// Pythonif (ext === '.py') {const importMatches = content.match(/^import\s+(\w+)|^from\s+(\w+)/gm);if (importMatches) {importMatches.forEach(match => {const dep = match.match(/(?:import|from)\s+(\w+)/)[1];dependencies.push(dep);});}}return dependencies;
}
性能优化
1. 大文件处理
对于大文件,不应该一次性读取到内存:
async function analyzeLargeFile(filePath) {const stats = fs.statSync(filePath);const fileSize = stats.size;const maxSize = 10 * 1024 * 1024; // 10MBif (fileSize > maxSize) {// 只读取文件头进行分析const buffer = Buffer.alloc(1024);const fd = fs.openSync(filePath, 'r');fs.readSync(fd, buffer, 0, 1024, 0);fs.closeSync(fd);return {tooLarge: true,size: fileSize,preview: analyzeFileHeader(buffer)};}// 小文件正常处理return analyzeFile(filePath);
}
2. 流式读取
const readline = require('readline');
const fs = require('fs');async function analyzeLargeTextFile(filePath) {const fileStream = fs.createReadStream(filePath);const rl = readline.createInterface({input: fileStream,crlfDelay: Infinity});let lineCount = 0;let wordCount = 0;for await (const line of rl) {lineCount++;wordCount += line.split(/\s+/).filter(w => w.length > 0).length;}return {lines: lineCount,words: wordCount};
}
3. 异步处理
async function analyzeFileAsync(filePath) {// 使用 Promise.all 并行处理const [stats, buffer] = await Promise.all([fs.promises.stat(filePath),fs.promises.readFile(filePath)]);// 异步分析const analysis = await Promise.resolve(analyzeFileContent(buffer, stats));return analysis;
}
4. 缓存机制
const analysisCache = new Map();function analyzeFileWithCache(filePath) {const stats = fs.statSync(filePath);const cacheKey = `${filePath}-${stats.mtime.getTime()}`;if (analysisCache.has(cacheKey)) {return analysisCache.get(cacheKey);}const analysis = analyzeFile(filePath);analysisCache.set(cacheKey, analysis);// 限制缓存大小if (analysisCache.size > 100) {const firstKey = analysisCache.keys().next().value;analysisCache.delete(firstKey);}return analysis;
}
最佳实践
1. 错误处理
async function analyzeFile(filePath) {try {// 检查文件是否存在if (!fs.existsSync(filePath)) {throw new Error('文件不存在');}// 检查文件大小const stats = fs.statSync(filePath);if (stats.size === 0) {throw new Error('文件为空');}// 执行分析const buffer = fs.readFileSync(filePath);// ...} catch (error) {// 详细的错误信息console.error('分析文件失败:', {filePath,error: error.message,stack: error.stack});// 用户友好的错误提示return {success: false,error: error.message};}
}
2. 文件类型验证
function isValidTextFile(ext) {const validExtensions = ['.txt', '.md', '.json', '.js', '.html', '.css','.py', '.java', '.cpp', '.c', '.go', '.rs', '.php'];return validExtensions.includes(ext.toLowerCase());
}function isValidImageFile(ext) {const validExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'];return validExtensions.includes(ext.toLowerCase());
}
3. 内存管理
function analyzeFileSafely(filePath) {const maxMemory = 50 * 1024 * 1024; // 50MBconst stats = fs.statSync(filePath);if (stats.size > maxMemory) {// 使用流式处理return analyzeLargeFile(filePath);}// 正常处理return analyzeFile(filePath);
}
4. 用户反馈
async function analyzeFileWithProgress(filePath, onProgress) {const stats = fs.statSync(filePath);const totalSize = stats.size;let processedSize = 0;const stream = fs.createReadStream(filePath);stream.on('data', (chunk) => {processedSize += chunk.length;const progress = (processedSize / totalSize) * 100;onProgress(progress);});stream.on('end', () => {onProgress(100);});// 分析逻辑...
}
常见问题
1. 大文件导致内存溢出
问题:读取大文件时内存溢出。
解决方案:
- 限制文件大小
- 使用流式读取
- 只读取文件头进行分析
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MBif (stats.size > MAX_FILE_SIZE) {alert('文件过大,无法完整分析');return;
}
2. 编码检测不准确
问题:无法准确检测文件编码。
解决方案:
- 使用专业的编码检测库
- 提供手动选择编码的选项
- 尝试多种编码方式
// 使用 chardet 库
const chardet = require('chardet');
const detected = chardet.detect(buffer);
3. 图片尺寸解析失败
问题:某些图片格式无法解析尺寸。
解决方案:
- 使用图片处理库(如
sharp、jimp) - 在浏览器中加载图片获取尺寸
// 使用 sharp 库
const sharp = require('sharp');async function getImageSize(filePath) {const metadata = await sharp(filePath).metadata();return {width: metadata.width,height: metadata.height};
}
4. 性能问题
问题:分析大文件时界面卡顿。
解决方案:
- 使用 Web Worker 进行后台处理
- 显示加载进度
- 异步处理
// 使用 Web Worker
const worker = new Worker('file-analyzer-worker.js');
worker.postMessage({ filePath, buffer });
worker.onmessage = (e) => {displayResults(e.data);
};
5. 二进制文件误判
问题:二进制文件被当作文本文件处理。
解决方案:
- 检查文件头
- 检测二进制字符
- 限制文本文件大小
function isBinaryFile(buffer) {// 检查是否包含 NULL 字节if (buffer.indexOf(0) !== -1) {return true;}// 检查控制字符比例let controlChars = 0;for (let i = 0; i < Math.min(buffer.length, 512); i++) {if (buffer[i] < 32 && buffer[i] !== 9 && buffer[i] !== 10 && buffer[i] !== 13) {controlChars++;}}return controlChars / buffer.length > 0.3;
}
总结
通过本文,我们学习了:
- ✅ 文件编码检测:通过 BOM 和字符分析检测文件编码
- ✅ 文本文件分析:统计行数、字数、字符数、段落数
- ✅ 图片文件分析:通过文件头解析图片尺寸和格式
- ✅ UI 设计:创建美观的分析结果展示界面
- ✅ 性能优化:处理大文件和提升分析速度
- ✅ 功能扩展:代码分析、JSON 验证、Markdown 分析等
关键要点
- 文件头解析是识别文件格式的关键技术
- 位运算用于解析二进制文件格式
- 正则表达式用于文本统计和分析
- 错误处理对于提升用户体验至关重要
- 性能优化需要考虑大文件和内存管理
下一步学习
- 文件选择功能 - 基础文件操作
- 文件系统操作 - 更多文件操作技巧
- IPC 通信详解 - 进程间通信
祝您开发愉快! 🚀
最后更新:2025年11月10日
Electron 版本:39.1.1
Node.js 版本:20.17.0
