欢迎光临丧葬服务网
详情描述

一、浏览器环境

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。