Electron 智能文件分析器开发实战适配鸿蒙
智能文件分析器是一个强大的文件信息提取工具,能够自动识别文件类型并提取详细的统计信息。该功能在文件选择的基础上,进一步提供了深度的文件分析能力。✅文件编码检测:通过 BOM 和字符分析检测文件编码✅文本文件分析:统计行数、字数、字符数、段落数✅图片文件分析:通过文件头解析图片尺寸和格式✅UI 设计:创建美观的分析结果展示界面✅性能优化:处理大文件和提升分析速度✅功能扩展:代码分析、JSON 验证、
·
Electron 智能文件分析器开发实战适配鸿蒙
目录
功能概述
智能文件分析器是一个强大的文件信息提取工具,能够自动识别文件类型并提取详细的统计信息。该功能在文件选择的基础上,进一步提供了深度的文件分析能力。
核心功能
-
文件类型识别
- 根据扩展名显示对应的文件图标
- 支持 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 BF
if (buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF) {
return 'UTF-8 (BOM)';
}
// UTF-16 LE BOM: FF FE
if (buffer[0] === 0xFF && buffer[1] === 0xFE) {
return 'UTF-16 LE';
}
// UTF-16 BE BOM: FE FF
if (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) {
// PNG
if (buffer[0] === 0x89 && buffer[1] === 0x50 &&
buffer[2] === 0x4E && buffer[3] === 0x47) {
return parsePNG(buffer);
}
// JPEG
if (buffer[0] === 0xFF && buffer[1] === 0xD8) {
return parseJPEG(buffer);
}
// GIF
if (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/TypeScript
if (['.js', '.ts', '.jsx', '.tsx'].includes(ext)) {
const matches = content.match(/function\s+\w+|const\s+\w+\s*=\s*\(/g);
return matches ? matches.length : 0;
}
// Python
if (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/TypeScript
if (['.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);
});
}
}
// Python
if (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; // 10MB
if (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; // 50MB
const 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; // 10MB
if (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 分析等
关键要点
- 文件头解析是识别文件格式的关键技术
- 位运算用于解析二进制文件格式
- 正则表达式用于文本统计和分析
- 错误处理对于提升用户体验至关重要
- 性能优化需要考虑大文件和内存管理
下一步学习
祝您开发愉快! 🚀
最后更新:2025年11月10日
Electron 版本:39.1.1
Node.js 版本:20.17.0
更多推荐

所有评论(0)