主题
Node.js
目录
- 获取当前工作目录
js
console.log(process.cwd());- 获取当前文件所在目录
js
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
console.log(__dirname); // 当前文件所在目录文件
判断文件或目录是否存在
js
fs.existsSync(path);判断文件还是目录
js
const fullPath = path.join(dirPath, file);
const stat = fs.statSync(fullPath);
console.log(stat.isDirectory());获取文件绝对路径
js
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const filePath = path.resolve(__dirname, './file.txt');
// 或者 const filePath = path.join(__dirname, 'file.txt');获取文件内容
js
const mdData = fs.readFileSync(filePath, 'utf8');写入文件内容
js
// 写入/覆盖
fs.writeFileSync(STORE_PATH, JSON.stringify({}, null, 2));
// 追加
fs.appendFileSync(STORE_PATH, JSON.stringify(data, null, 2));读取并解析 JSON 文件
js
import fs from 'fs';
import path from 'path';
const readJsonFile = (filePath) => {
const fullPath = path.resolve(filePath);
const content = fs.readFileSync(fullPath, 'utf8');
return JSON.parse(content);
};写入 JSON 文件到指定目录
js
import fs from 'fs';
import path from 'path';
const writeJsonFile = (dirPath, fileName, data) => {
// 确保目录存在
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
const fullPath = path.join(dirPath, fileName);
fs.writeFileSync(fullPath, JSON.stringify(data, null, 2), 'utf8');
return fullPath;
};递归搜索目录中的文件
js
import fs from 'fs';
import path from 'path';
const findFiles = (dirPath, extensions = [], excludeDirs = []) => {
const files = [];
const traverse = (currentPath) => {
const items = fs.readdirSync(currentPath);
for (const item of items) {
const fullPath = path.join(currentPath, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
// 跳过排除的目录
if (!excludeDirs.includes(item)) {
traverse(fullPath);
}
} else {
// 按扩展名过滤
if (extensions.length === 0 || extensions.includes(path.extname(item))) {
files.push(fullPath);
}
}
}
};
traverse(dirPath);
return files;
};递归搜索目录(带文件信息)
js
import fs from 'fs';
import path from 'path';
const scanDirectory = (dirPath, options = {}) => {
const {
extensions = [], // 文件扩展名过滤,如 ['.js', '.ts']
excludeDirs = [], // 排除的目录名
includeHidden = false // 是否包含隐藏文件
} = options;
const results = [];
const traverse = (currentPath, depth = 0) => {
const items = fs.readdirSync(currentPath);
for (const item of items) {
// 跳过隐藏文件
if (!includeHidden && item.startsWith('.')) {
continue;
}
const fullPath = path.join(currentPath, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
if (!excludeDirs.includes(item)) {
traverse(fullPath, depth + 1);
}
} else {
// 扩展名过滤
if (extensions.length === 0 || extensions.includes(path.extname(item))) {
results.push({
path: fullPath,
name: item,
basename: path.basename(item, path.extname(item)),
ext: path.extname(item),
size: stat.size,
depth
});
}
}
}
};
traverse(dirPath);
return results;
};获取文件名和扩展名
js
import path from 'path';
const filePath = '/path/to/file/example.txt';
// 获取完整文件名
const fileName = path.basename(filePath); // 'example.txt'
// 获取不带扩展名的文件名
const baseName = path.basename(filePath, path.extname(filePath)); // 'example'
// 获取扩展名
const extName = path.extname(filePath); // '.txt'
// 获取目录名
const dirName = path.dirname(filePath); // '/path/to/file'
// 解析路径(包含所有信息)
const parsed = path.parse(filePath);
// {
// root: '/',
// dir: '/path/to/file',
// base: 'example.txt',
// ext: '.txt',
// name: 'example'
// }复制文件到目标目录
js
import fs from 'fs';
import path from 'path';
const copyFile = (sourcePath, targetDir) => {
// 确保目标目录存在
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
const fileName = path.basename(sourcePath);
const targetPath = path.join(targetDir, fileName);
fs.copyFileSync(sourcePath, targetPath);
return targetPath;
};删除文件或目录
js
import fs from 'fs';
import path from 'path';
const deletePath = (targetPath) => {
if (!fs.existsSync(targetPath)) {
return;
}
const stat = fs.statSync(targetPath);
if (stat.isDirectory()) {
// 递归删除目录
fs.rmSync(targetPath, { recursive: true, force: true });
} else {
// 删除文件
fs.unlinkSync(targetPath);
}
};重命名/移动文件
js
import fs from 'fs';
import path from 'path';
const moveFile = (sourcePath, targetPath) => {
// 确保目标目录存在
const targetDir = path.dirname(targetPath);
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
fs.renameSync(sourcePath, targetPath);
return targetPath;
};获取文件统计信息
js
import fs from 'fs';
const getFileInfo = (filePath) => {
const stat = fs.statSync(filePath);
return {
isFile: stat.isFile(),
isDirectory: stat.isDirectory(),
size: stat.size, // 字节
created: stat.birthtime, // 创建时间
modified: stat.mtime, // 修改时间
accessed: stat.atime // 访问时间
};
};路径拼接与解析
js
import path from 'path';
// 拼接路径(自动处理分隔符)
const joinedPath = path.join('/foo', 'bar', 'baz/asdf', 'quux');
// '/foo/bar/baz/asdf/quux'
// 解析为绝对路径
const resolvedPath = path.resolve('src/file.txt');
// '/current/working/dir/src/file.txt'
// 获取相对路径
const relativePath = path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb');
// '../../impl/bbb'
// 规范化路径(处理 .. 和 .)
const normalizedPath = path.normalize('/foo/bar//baz/asdf/quux/..');
// '/foo/bar/baz/asdf'文件系统工具函数集合
以下是一个完整的文件系统工具类,整合上述常用功能:
js
import fs from 'fs';
import path from 'path';
class FsUtils {
/**
* 读取 JSON 文件
* @param {string} filePath - 文件路径
* @returns {Object} 解析后的 JSON 对象
*/
static readJson(filePath) {
const content = fs.readFileSync(filePath, 'utf8');
return JSON.parse(content);
}
/**
* 写入 JSON 文件
* @param {string} dirPath - 目录路径
* @param {string} fileName - 文件名
* @param {Object} data - 要写入的数据
* @returns {string} 写入文件的完整路径
*/
static writeJson(dirPath, fileName, data) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
const fullPath = path.join(dirPath, fileName);
fs.writeFileSync(fullPath, JSON.stringify(data, null, 2), 'utf8');
return fullPath;
}
/**
* 递归搜索目录
* @param {string} dirPath - 目录路径
* @param {Object} options - 配置选项
* @returns {Array} 文件信息数组
*/
static scan(dirPath, options = {}) {
const {
extensions = [],
excludeDirs = ['node_modules', '.git'],
includeHidden = false
} = options;
const results = [];
const traverse = (currentPath, depth = 0) => {
const items = fs.readdirSync(currentPath);
for (const item of items) {
if (!includeHidden && item.startsWith('.')) continue;
const fullPath = path.join(currentPath, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
if (!excludeDirs.includes(item)) {
traverse(fullPath, depth + 1);
}
} else {
if (extensions.length === 0 || extensions.includes(path.extname(item))) {
results.push({
path: fullPath,
name: item,
basename: path.basename(item, path.extname(item)),
ext: path.extname(item),
size: stat.size,
depth
});
}
}
}
};
traverse(dirPath);
return results;
}
/**
* 复制文件
* @param {string} sourcePath - 源文件路径
* @param {string} targetDir - 目标目录
* @returns {string} 目标文件完整路径
*/
static copy(sourcePath, targetDir) {
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
const targetPath = path.join(targetDir, path.basename(sourcePath));
fs.copyFileSync(sourcePath, targetPath);
return targetPath;
}
/**
* 删除文件或目录
* @param {string} targetPath - 目标路径
*/
static delete(targetPath) {
if (!fs.existsSync(targetPath)) return;
const stat = fs.statSync(targetPath);
if (stat.isDirectory()) {
fs.rmSync(targetPath, { recursive: true, force: true });
} else {
fs.unlinkSync(targetPath);
}
}
/**
* 移动/重命名文件
* @param {string} sourcePath - 源文件路径
* @param {string} targetPath - 目标路径
* @returns {string} 目标路径
*/
static move(sourcePath, targetPath) {
const targetDir = path.dirname(targetPath);
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
fs.renameSync(sourcePath, targetPath);
return targetPath;
}
/**
* 获取文件信息
* @param {string} filePath - 文件路径
* @returns {Object} 文件信息对象
*/
static getInfo(filePath) {
const stat = fs.statSync(filePath);
return {
isFile: stat.isFile(),
isDirectory: stat.isDirectory(),
size: stat.size,
created: stat.birthtime,
modified: stat.mtime,
accessed: stat.atime
};
}
/**
* 路径解析工具
* @param {string} filePath - 文件路径
* @returns {Object} 路径信息对象
*/
static parsePath(filePath) {
const parsed = path.parse(filePath);
return {
full: filePath,
dir: parsed.dir,
base: parsed.base,
name: parsed.name,
ext: parsed.ext,
absolute: path.resolve(filePath)
};
}
}
export default FsUtils;常用路径处理
js
import path from 'path';
import { fileURLToPath } from 'url';
// ES Module 中获取 __dirname 和 __filename
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// 获取项目根目录(假设当前文件在 src/ 目录下)
const rootDir = path.resolve(__dirname, '../');
// 路径拼接示例
const configPath = path.join(rootDir, 'config', 'settings.json');
const outputPath = path.resolve(rootDir, './dist/bundle.js');
// 路径分割
const parts = path.parse('/Users/name/project/src/file.js');
// parts.dir -> '/Users/name/project/src'
// parts.base -> 'file.js'
// parts.name -> 'file'
// parts.ext -> '.js'使用文档
引入方式
将上述 FsUtils 类保存为独立文件,然后在项目中引入:
js
import FsUtils from './utils/FsUtils.js';功能使用示例
读取 JSON 配置文件
js
// 读取 package.json
const pkg = FsUtils.readJson('./package.json');
console.log(pkg.name); // 项目名称
console.log(pkg.version); // 项目版本
console.log(pkg.dependencies); // 依赖列表写入配置文件
js
// 生成配置文件到 config 目录
const settings = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retry: 3
};
const filePath = FsUtils.writeJson('./config', 'settings.json', settings);
// 返回: '/project/config/settings.json'扫描项目中的特定文件
js
// 查找所有 JavaScript 文件
const jsFiles = FsUtils.scan('./src', {
extensions: ['.js', '.jsx']
});
console.log(jsFiles);
// [
// { path: '/src/index.js', name: 'index.js', basename: 'index', ext: '.js', size: 1024, depth: 0 },
// { path: '/src/utils/helper.js', name: 'helper.js', basename: 'helper', ext: '.js', size: 2048, depth: 1 }
// ]
// 查找所有文件(不限制扩展名)
const allFiles = FsUtils.scan('./src', {
excludeDirs: ['node_modules', '.git', 'dist']
});复制文件到构建目录
js
// 复制静态资源到 dist 目录
const sourceFile = './src/assets/logo.png';
const targetDir = './dist/assets';
const copiedPath = FsUtils.copy(sourceFile, targetDir);
// 返回: '/dist/assets/logo.png'清理构建目录
js
// 删除旧的构建目录
FsUtils.delete('./dist');
// 删除单个文件
FsUtils.delete('./output.log');移动临时文件
js
// 将生成的临时文件移动到正式目录
FsUtils.move('./temp/result.json', './output/results.json');获取文件详细信息
js
// 检查文件信息
const info = FsUtils.getInfo('./src/app.js');
if (info.isFile) {
console.log(`文件大小: ${(info.size / 1024).toFixed(2)} KB`);
console.log(`修改时间: ${info.modified}`);
}解析文件路径
js
// 获取路径的各个组成部分
const parsed = FsUtils.parsePath('/project/src/components/Button.jsx');
console.log(parsed.full); // '/project/src/components/Button.jsx'
console.log(parsed.dir); // '/project/src/components'
console.log(parsed.base); // 'Button.jsx'
console.log(parsed.name); // 'Button'
console.log(parsed.ext); // '.jsx'
console.log(parsed.absolute); // '/project/src/components/Button.jsx'实际应用场景
场景一:构建工具中的文件处理
js
import FsUtils from './utils/FsUtils.js';
// 1. 清理旧构建
FsUtils.delete('./dist');
// 2. 复制静态资源
FsUtils.copy('./src/public/index.html', './dist');
FsUtils.copy('./src/public/favicon.ico', './dist');
// 3. 读取源文件配置
const config = FsUtils.readJson('./src/config.json');
// 4. 扫描所有需要处理的源文件
const sourceFiles = FsUtils.scan('./src', {
extensions: ['.js', '.css'],
excludeDirs: ['tests', 'examples']
});
// 5. 处理每个文件
sourceFiles.forEach(file => {
const relativePath = file.path.replace('/src/', '');
const outputPath = `./dist/${relativePath}`;
// 执行编译、压缩等操作...
});场景二:项目文档生成工具
js
import FsUtils from './utils/FsUtils.js';
// 1. 扫描所有 Markdown 文件
const mdFiles = FsUtils.scan('./docs', {
extensions: ['.md'],
excludeDirs: ['node_modules', '.git']
});
// 2. 构建文档树结构
const docTree = {
title: '项目文档',
files: mdFiles.map(file => ({
path: file.path,
name: file.basename,
title: file.basename.replace(/-/g, ' ')
}))
};
// 3. 输出文档索引文件
FsUtils.writeJson('./docs', 'index.json', docTree);场景三:静态资源管理
js
import FsUtils from './utils/FsUtils.js';
// 1. 扫描图片目录
const images = FsUtils.scan('./src/assets/images', {
extensions: ['.png', '.jpg', '.svg', '.webp']
});
// 2. 按类型分类
const imagesByType = {
png: images.filter(img => img.ext === '.png'),
jpg: images.filter(img => img.ext === '.jpg'),
svg: images.filter(img => img.ext === '.svg'),
webp: images.filter(img => img.ext === '.webp')
};
// 3. 生成资源清单
const manifest = {
images: images.map(img => ({
src: img.path,
name: img.basename,
size: img.size,
type: img.ext.slice(1)
}))
};
FsUtils.writeJson('./dist', 'asset-manifest.json', manifest);场景四:配置文件迁移
js
import FsUtils from './utils/FsUtils.js';
// 读取旧配置
const oldConfig = FsUtils.readJson('./config.old.json');
// 转换配置格式
const newConfig = {
version: '2.0',
server: {
host: oldConfig.hostname,
port: oldConfig.port
},
database: {
url: oldConfig.dbUrl,
pool: oldConfig.dbPool
}
};
// 备份旧配置
FsUtils.move('./config.old.json', './backup/config.old.json');
// 写入新配置
FsUtils.writeJson('./config', 'app.json', newConfig);场景五:批量文件重命名
js
import FsUtils from './utils/FsUtils.js';
// 扫描需要重命名的文件
const files = FsUtils.scan('./src/components', {
extensions: ['.js']
});
// 批量重命名(添加前缀)
files.forEach(file => {
const oldPath = file.path;
const newPath = file.path.replace(file.name, `Comp_${file.name}`);
FsUtils.move(oldPath, newPath);
});注意事项
路径处理
- 所有路径参数支持相对路径和绝对路径
- 相对路径基于当前工作目录(
process.cwd())解析 - Windows 路径分隔符会被自动处理
目录自动创建
writeJson、copy、move方法会自动创建目标目录- 使用
recursive: true参数确保父目录存在
文件覆盖
writeJson和copy操作会覆盖已存在的文件- 建议在覆盖前检查文件是否存在
错误处理
- 同步方法在出错时会抛出异常
- 建议使用 try-catch 包裹调用:
jstry { const data = FsUtils.readJson('./config.json'); } catch (error) { console.error('读取配置失败:', error.message); }性能考虑
- 扫描大目录时注意内存使用
- 可以通过
excludeDirs排除不必要的目录 - 深度遍历可能影响性能