一、浏览器环境
1. File API(用户手动选择文件)
// 单个文件读取
document.getElementById('fileInput').addEventListener('change', async (e) => {
const file = e.target.files[0];
const text = await file.text();
console.log('文件内容:', text);
});
// 多个文件读取
document.getElementById('filesInput').addEventListener('change', async (e) => {
const files = e.target.files;
for (const file of files) {
const content = await file.text();
console.log(`${file.name}:`, content);
}
});
// 读取为不同格式
async function readFile(file) {
// 读取为文本
const text = await file.text();
// 读取为ArrayBuffer(二进制)
const arrayBuffer = await file.arrayBuffer();
// 读取为DataURL(base64)
const dataURL = await new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target.result);
reader.readAsDataURL(file);
});
// 读取为二进制字符串
const binaryString = await new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target.result);
reader.readAsBinaryString(file);
});
}
2. 拖放API读取文件
<div id="dropZone" style="border: 2px dashed #ccc; padding: 20px;">
拖放文件到这里
</div>
<script>
const dropZone = document.getElementById('dropZone');
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.style.backgroundColor = '#f0f0f0';
});
dropZone.addEventListener('drop', async (e) => {
e.preventDefault();
dropZone.style.backgroundColor = '';
const items = e.dataTransfer.items;
for (const item of items) {
if (item.kind === 'file') {
const file = item.getAsFile();
console.log('文件名:', file.name);
console.log('文件大小:', file.size);
console.log('内容:', await file.text());
}
}
});
</script>
3. 使用 <input type="file" webkitdirectory> 读取目录
<input type="file" id="dirInput" webkitdirectory multiple>
<script>
document.getElementById('dirInput').addEventListener('change', async (e) => {
const files = e.target.files;
console.log(`选择了 ${files.length} 个文件`);
for (const file of files) {
// file.webkitRelativePath 包含相对路径
console.log(`路径: ${file.webkitRelativePath}`);
console.log(`内容: ${await file.text()}`);
}
});
</script>
4. 文件系统访问API(现代浏览器)
// 请求文件句柄
async function getFileHandle() {
try {
const [fileHandle] = await window.showOpenFilePicker({
types: [{
description: '文本文件',
accept: {'text/plain': ['.txt']}
}],
multiple: false
});
const file = await fileHandle.getFile();
return await file.text();
} catch (err) {
console.log('用户取消了选择');
}
}
// 请求目录句柄
async function getDirectoryHandle() {
try {
const dirHandle = await window.showDirectoryPicker();
// 遍历目录
for await (const [name, handle] of dirHandle.entries()) {
if (handle.kind === 'file') {
const file = await handle.getFile();
console.log(`文件: ${name}, 大小: ${file.size}`);
} else if (handle.kind === 'directory') {
console.log(`目录: ${name}`);
}
}
} catch (err) {
console.log('用户取消了选择');
}
}
二、Node.js 环境
1. fs 模块同步方法
const fs = require('fs');
const path = require('path');
// 同步读取文件
try {
const content = fs.readFileSync('./file.txt', 'utf8');
console.log('文件内容:', content);
} catch (err) {
console.error('读取文件失败:', err);
}
// 同步读取目录
try {
const files = fs.readdirSync('./directory');
console.log('目录内容:', files);
// 获取文件详细信息
files.forEach(file => {
const stats = fs.statSync(path.join('./directory', file));
console.log(`${file} - 类型: ${stats.isDirectory() ? '目录' : '文件'}`);
});
} catch (err) {
console.error('读取目录失败:', err);
}
2. fs 模块异步方法
const fs = require('fs').promises;
// 异步读取文件
async function readFileAsync() {
try {
const content = await fs.readFile('./file.txt', 'utf8');
console.log('文件内容:', content);
} catch (err) {
console.error('读取文件失败:', err);
}
}
// 异步读取目录
async function readDirectoryAsync(dirPath) {
try {
const files = await fs.readdir(dirPath, { withFileTypes: true });
for (const file of files) {
if (file.isDirectory()) {
console.log(`[目录] ${file.name}`);
await readDirectoryAsync(path.join(dirPath, file.name));
} else {
console.log(`[文件] ${file.name}`);
}
}
} catch (err) {
console.error('读取目录失败:', err);
}
}
3. 使用流读取大文件
const fs = require('fs');
// 读取流
function readLargeFile(filePath) {
const readStream = fs.createReadStream(filePath, { encoding: 'utf8' });
readStream.on('data', (chunk) => {
console.log('收到数据块:', chunk.length, '字节');
// 处理数据
});
readStream.on('end', () => {
console.log('文件读取完成');
});
readStream.on('error', (err) => {
console.error('读取错误:', err);
});
}
// 逐行读取
const readline = require('readline');
function readLineByLine(filePath) {
const readStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: readStream,
crlfDelay: Infinity
});
rl.on('line', (line) => {
console.log('行内容:', line);
});
rl.on('close', () => {
console.log('文件读取完毕');
});
}
4. 递归遍历目录
const fs = require('fs');
const path = require('path');
function walkDir(dirPath) {
const results = [];
function walk(currentPath) {
const files = fs.readdirSync(currentPath);
for (const file of files) {
const filePath = path.join(currentPath, file);
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
results.push({
type: 'directory',
path: filePath,
name: file
});
walk(filePath);
} else {
results.push({
type: 'file',
path: filePath,
name: file,
size: stats.size,
modified: stats.mtime
});
}
}
}
walk(dirPath);
return results;
}
// 使用示例
const allFiles = walkDir('./my-directory');
console.log('找到文件:', allFiles.length);
5. 使用 glob 模式匹配文件
const glob = require('glob');
// 查找所有 .js 文件
glob('**/*.js', (err, files) => {
if (err) throw err;
console.log('找到的JS文件:', files);
});
// 同步版本
const jsFiles = glob.sync('src/**/*.{js,ts}');
console.log('源文件:', jsFiles);
三、Electron 环境(混合)
// 主进程
const { ipcMain, dialog } = require('electron');
const fs = require('fs');
// 打开文件对话框
ipcMain.handle('open-file', async (event) => {
const result = await dialog.showOpenDialog({
properties: ['openFile']
});
if (!result.canceled) {
const filePath = result.filePaths[0];
const content = fs.readFileSync(filePath, 'utf8');
return { path: filePath, content };
}
return null;
});
// 渲染进程
const { ipcRenderer } = require('electron');
async function loadFile() {
const fileData = await ipcRenderer.invoke('open-file');
if (fileData) {
console.log('文件路径:', fileData.path);
console.log('文件内容:', fileData.content);
}
}
选择建议
浏览器环境:
- 用户交互:使用 File API 或拖放 API
- 现代应用:使用 File System Access API(需要用户授权)
- 兼容性:File API 支持最广泛
Node.js 环境:
- 小文件:使用同步或异步的
fs.readFile
- 大文件:使用流处理
- 目录操作:使用
fs.readdir 配合递归
- 性能要求:考虑使用异步方法
Electron:
- 结合两者的优势
- 主进程使用 Node.js fs 模块
- 渲染进程使用浏览器 API
安全注意事项
浏览器中不能直接访问任意本地文件(需要用户选择)
Node.js 中注意路径安全性,避免目录遍历攻击
处理大文件时使用流,避免内存溢出
异步操作注意错误处理
根据具体场景选择合适的方法,如果需要完整的文件系统访问权限,推荐使用 Node.js 或 Electron。