Skip to content

文件系统 API

概述

Trae 文件系统 API 提供了完整的文件和目录操作功能,支持本地文件系统、远程文件系统和虚拟文件系统。开发者可以通过 API 进行文件读写、目录管理、文件监控等操作。

核心概念

URI 和路径

typescript
import { Uri } from '@trae/api';

// 创建文件 URI
const fileUri = Uri.file('/path/to/file.txt');
const httpUri = Uri.parse('https://example.com/file.txt');
const customUri = Uri.parse('custom://scheme/path');

// URI 属性
console.log(fileUri.scheme); // 'file'
console.log(fileUri.path);   // '/path/to/file.txt'
console.log(fileUri.fsPath); // 平台特定的文件系统路径

文件类型

typescript
enum FileType {
  Unknown = 0,
  File = 1,
  Directory = 2,
  SymbolicLink = 64
}

interface FileStat {
  type: FileType;
  ctime: number;  // 创建时间
  mtime: number;  // 修改时间
  size: number;   // 文件大小
}

API 参考

文件读写操作

readFile()

读取文件内容:

typescript
import { workspace } from '@trae/api';

// 读取文本文件
const readTextFile = async (uri: Uri): Promise<string> => {
  try {
    const content = await workspace.fs.readFile(uri);
    return Buffer.from(content).toString('utf8');
  } catch (error) {
    console.error('Failed to read file:', error);
    throw error;
  }
};

// 读取二进制文件
const readBinaryFile = async (uri: Uri): Promise<Uint8Array> => {
  return await workspace.fs.readFile(uri);
};

// 示例使用
const fileUri = Uri.file('/path/to/file.txt');
const content = await readTextFile(fileUri);
console.log('File content:', content);

writeFile()

写入文件内容:

typescript
// 写入文本文件
const writeTextFile = async (uri: Uri, content: string): Promise<void> => {
  try {
    const buffer = Buffer.from(content, 'utf8');
    await workspace.fs.writeFile(uri, buffer);
  } catch (error) {
    console.error('Failed to write file:', error);
    throw error;
  }
};

// 写入二进制文件
const writeBinaryFile = async (uri: Uri, content: Uint8Array): Promise<void> => {
  await workspace.fs.writeFile(uri, content);
};

// 示例使用
const newFileUri = Uri.file('/path/to/new-file.txt');
await writeTextFile(newFileUri, 'Hello, World!');

目录操作

readDirectory()

读取目录内容:

typescript
// 读取目录
const readDirectory = async (uri: Uri): Promise<[string, FileType][]> => {
  try {
    return await workspace.fs.readDirectory(uri);
  } catch (error) {
    console.error('Failed to read directory:', error);
    return [];
  }
};

// 递归读取目录
const readDirectoryRecursive = async (uri: Uri): Promise<Uri[]> => {
  const files: Uri[] = [];
  
  const entries = await workspace.fs.readDirectory(uri);
  
  for (const [name, type] of entries) {
    const childUri = Uri.joinPath(uri, name);
    
    if (type === FileType.File) {
      files.push(childUri);
    } else if (type === FileType.Directory) {
      const childFiles = await readDirectoryRecursive(childUri);
      files.push(...childFiles);
    }
  }
  
  return files;
};

// 示例使用
const dirUri = Uri.file('/path/to/directory');
const entries = await readDirectory(dirUri);
console.log('Directory entries:', entries);

createDirectory()

创建目录:

typescript
// 创建单个目录
const createDirectory = async (uri: Uri): Promise<void> => {
  try {
    await workspace.fs.createDirectory(uri);
  } catch (error) {
    console.error('Failed to create directory:', error);
    throw error;
  }
};

// 创建嵌套目录
const createDirectoryRecursive = async (uri: Uri): Promise<void> => {
  const parts = uri.path.split('/').filter(part => part.length > 0);
  let currentPath = uri.scheme === 'file' ? '/' : '';
  
  for (const part of parts) {
    currentPath = currentPath.endsWith('/') ? currentPath + part : currentPath + '/' + part;
    const currentUri = Uri.file(currentPath);
    
    try {
      await workspace.fs.stat(currentUri);
    } catch {
      await workspace.fs.createDirectory(currentUri);
    }
  }
};

文件和目录管理

stat()

获取文件或目录信息:

typescript
// 获取文件统计信息
const getFileStat = async (uri: Uri): Promise<FileStat | null> => {
  try {
    return await workspace.fs.stat(uri);
  } catch (error) {
    console.error('Failed to get file stat:', error);
    return null;
  }
};

// 检查文件是否存在
const fileExists = async (uri: Uri): Promise<boolean> => {
  try {
    await workspace.fs.stat(uri);
    return true;
  } catch {
    return false;
  }
};

// 检查是否为目录
const isDirectory = async (uri: Uri): Promise<boolean> => {
  try {
    const stat = await workspace.fs.stat(uri);
    return stat.type === FileType.Directory;
  } catch {
    return false;
  }
};

delete()

删除文件或目录:

typescript
// 删除文件
const deleteFile = async (uri: Uri): Promise<void> => {
  try {
    await workspace.fs.delete(uri, { recursive: false, useTrash: false });
  } catch (error) {
    console.error('Failed to delete file:', error);
    throw error;
  }
};

// 删除目录(递归)
const deleteDirectory = async (uri: Uri, useTrash = true): Promise<void> => {
  try {
    await workspace.fs.delete(uri, { recursive: true, useTrash });
  } catch (error) {
    console.error('Failed to delete directory:', error);
    throw error;
  }
};

rename()

重命名或移动文件:

typescript
// 重命名文件
const renameFile = async (oldUri: Uri, newUri: Uri): Promise<void> => {
  try {
    await workspace.fs.rename(oldUri, newUri, { overwrite: false });
  } catch (error) {
    console.error('Failed to rename file:', error);
    throw error;
  }
};

// 移动文件到新目录
const moveFile = async (sourceUri: Uri, targetDirUri: Uri): Promise<Uri> => {
  const fileName = sourceUri.path.split('/').pop() || 'unknown';
  const targetUri = Uri.joinPath(targetDirUri, fileName);
  
  await workspace.fs.rename(sourceUri, targetUri, { overwrite: false });
  return targetUri;
};

文件复制

typescript
// 复制文件
const copyFile = async (sourceUri: Uri, targetUri: Uri): Promise<void> => {
  try {
    await workspace.fs.copy(sourceUri, targetUri, { overwrite: false });
  } catch (error) {
    console.error('Failed to copy file:', error);
    throw error;
  }
};

// 复制目录
const copyDirectory = async (sourceUri: Uri, targetUri: Uri): Promise<void> => {
  // 创建目标目录
  await workspace.fs.createDirectory(targetUri);
  
  // 读取源目录内容
  const entries = await workspace.fs.readDirectory(sourceUri);
  
  for (const [name, type] of entries) {
    const sourceChild = Uri.joinPath(sourceUri, name);
    const targetChild = Uri.joinPath(targetUri, name);
    
    if (type === FileType.File) {
      await copyFile(sourceChild, targetChild);
    } else if (type === FileType.Directory) {
      await copyDirectory(sourceChild, targetChild);
    }
  }
};

文件监控

文件系统监控器

typescript
import { workspace, FileSystemWatcher } from '@trae/api';

// 创建文件监控器
const createFileWatcher = (pattern: string): FileSystemWatcher => {
  const watcher = workspace.createFileSystemWatcher(pattern);
  
  // 监听文件创建
  watcher.onDidCreate(uri => {
    console.log('File created:', uri.fsPath);
    handleFileCreated(uri);
  });
  
  // 监听文件修改
  watcher.onDidChange(uri => {
    console.log('File changed:', uri.fsPath);
    handleFileChanged(uri);
  });
  
  // 监听文件删除
  watcher.onDidDelete(uri => {
    console.log('File deleted:', uri.fsPath);
    handleFileDeleted(uri);
  });
  
  return watcher;
};

// 示例:监控特定类型的文件
const jsWatcher = createFileWatcher('**/*.js');
const tsWatcher = createFileWatcher('**/*.{ts,tsx}');
const configWatcher = createFileWatcher('**/package.json');

高级文件监控

typescript
class FileMonitor {
  private watchers: Map<string, FileSystemWatcher> = new Map();
  private callbacks: Map<string, Function[]> = new Map();
  
  // 添加监控
  watch(pattern: string, callback: (uri: Uri, event: string) => void): void {
    if (!this.watchers.has(pattern)) {
      const watcher = workspace.createFileSystemWatcher(pattern);
      
      watcher.onDidCreate(uri => this.notifyCallbacks(pattern, uri, 'create'));
      watcher.onDidChange(uri => this.notifyCallbacks(pattern, uri, 'change'));
      watcher.onDidDelete(uri => this.notifyCallbacks(pattern, uri, 'delete'));
      
      this.watchers.set(pattern, watcher);
      this.callbacks.set(pattern, []);
    }
    
    this.callbacks.get(pattern)?.push(callback);
  }
  
  // 移除监控
  unwatch(pattern: string): void {
    const watcher = this.watchers.get(pattern);
    if (watcher) {
      watcher.dispose();
      this.watchers.delete(pattern);
      this.callbacks.delete(pattern);
    }
  }
  
  private notifyCallbacks(pattern: string, uri: Uri, event: string): void {
    const callbacks = this.callbacks.get(pattern) || [];
    callbacks.forEach(callback => callback(uri, event));
  }
  
  // 清理所有监控器
  dispose(): void {
    this.watchers.forEach(watcher => watcher.dispose());
    this.watchers.clear();
    this.callbacks.clear();
  }
}

文件搜索

基本搜索

typescript
// 搜索文件
const searchFiles = async (pattern: string, exclude?: string): Promise<Uri[]> => {
  return await workspace.findFiles(pattern, exclude);
};

// 示例搜索
const jsFiles = await searchFiles('**/*.js', '**/node_modules/**');
const configFiles = await searchFiles('**/config.{json,js,ts}');
const testFiles = await searchFiles('**/*.test.{js,ts}');

高级搜索

typescript
class FileSearcher {
  // 按内容搜索文件
  async searchByContent(pattern: RegExp, filePattern = '**/*'): Promise<{ uri: Uri; matches: RegExpMatchArray[] }[]> {
    const files = await workspace.findFiles(filePattern);
    const results = [];
    
    for (const file of files) {
      try {
        const content = await workspace.fs.readFile(file);
        const text = Buffer.from(content).toString('utf8');
        const matches = Array.from(text.matchAll(pattern));
        
        if (matches.length > 0) {
          results.push({ uri: file, matches });
        }
      } catch (error) {
        console.warn('Failed to read file for search:', file.fsPath);
      }
    }
    
    return results;
  }
  
  // 按文件大小搜索
  async searchBySize(minSize: number, maxSize: number): Promise<{ uri: Uri; size: number }[]> {
    const files = await workspace.findFiles('**/*');
    const results = [];
    
    for (const file of files) {
      try {
        const stat = await workspace.fs.stat(file);
        if (stat.size >= minSize && stat.size <= maxSize) {
          results.push({ uri: file, size: stat.size });
        }
      } catch (error) {
        console.warn('Failed to get file stat:', file.fsPath);
      }
    }
    
    return results;
  }
  
  // 按修改时间搜索
  async searchByModificationTime(since: Date): Promise<{ uri: Uri; mtime: number }[]> {
    const files = await workspace.findFiles('**/*');
    const results = [];
    const sinceTime = since.getTime();
    
    for (const file of files) {
      try {
        const stat = await workspace.fs.stat(file);
        if (stat.mtime >= sinceTime) {
          results.push({ uri: file, mtime: stat.mtime });
        }
      } catch (error) {
        console.warn('Failed to get file stat:', file.fsPath);
      }
    }
    
    return results;
  }
}

文件操作工具

批量操作

typescript
class BatchFileOperations {
  // 批量复制文件
  async copyFiles(operations: { source: Uri; target: Uri }[]): Promise<void> {
    const promises = operations.map(op => 
      workspace.fs.copy(op.source, op.target, { overwrite: false })
    );
    
    await Promise.all(promises);
  }
  
  // 批量删除文件
  async deleteFiles(uris: Uri[], useTrash = true): Promise<void> {
    const promises = uris.map(uri => 
      workspace.fs.delete(uri, { recursive: false, useTrash })
    );
    
    await Promise.all(promises);
  }
  
  // 批量重命名文件
  async renameFiles(operations: { oldUri: Uri; newUri: Uri }[]): Promise<void> {
    for (const op of operations) {
      await workspace.fs.rename(op.oldUri, op.newUri, { overwrite: false });
    }
  }
}

文件同步

typescript
class FileSynchronizer {
  // 同步两个目录
  async syncDirectories(sourceUri: Uri, targetUri: Uri): Promise<void> {
    const sourceFiles = await this.getAllFiles(sourceUri);
    const targetFiles = await this.getAllFiles(targetUri);
    
    // 复制新文件和更新的文件
    for (const sourceFile of sourceFiles) {
      const relativePath = this.getRelativePath(sourceUri, sourceFile);
      const targetFile = Uri.joinPath(targetUri, relativePath);
      
      const shouldCopy = await this.shouldCopyFile(sourceFile, targetFile);
      if (shouldCopy) {
        await this.ensureDirectoryExists(Uri.joinPath(targetUri, relativePath.split('/').slice(0, -1).join('/')));
        await workspace.fs.copy(sourceFile, targetFile, { overwrite: true });
      }
    }
    
    // 删除目标目录中多余的文件
    for (const targetFile of targetFiles) {
      const relativePath = this.getRelativePath(targetUri, targetFile);
      const sourceFile = Uri.joinPath(sourceUri, relativePath);
      
      if (!(await this.fileExists(sourceFile))) {
        await workspace.fs.delete(targetFile);
      }
    }
  }
  
  private async shouldCopyFile(sourceUri: Uri, targetUri: Uri): Promise<boolean> {
    try {
      const sourceStat = await workspace.fs.stat(sourceUri);
      const targetStat = await workspace.fs.stat(targetUri);
      
      return sourceStat.mtime > targetStat.mtime;
    } catch {
      return true; // 目标文件不存在,需要复制
    }
  }
  
  private async getAllFiles(uri: Uri): Promise<Uri[]> {
    const files: Uri[] = [];
    const entries = await workspace.fs.readDirectory(uri);
    
    for (const [name, type] of entries) {
      const childUri = Uri.joinPath(uri, name);
      
      if (type === FileType.File) {
        files.push(childUri);
      } else if (type === FileType.Directory) {
        const childFiles = await this.getAllFiles(childUri);
        files.push(...childFiles);
      }
    }
    
    return files;
  }
  
  private getRelativePath(baseUri: Uri, fileUri: Uri): string {
    return fileUri.path.substring(baseUri.path.length + 1);
  }
  
  private async fileExists(uri: Uri): Promise<boolean> {
    try {
      await workspace.fs.stat(uri);
      return true;
    } catch {
      return false;
    }
  }
  
  private async ensureDirectoryExists(uri: Uri): Promise<void> {
    try {
      await workspace.fs.stat(uri);
    } catch {
      await workspace.fs.createDirectory(uri);
    }
  }
}

错误处理

文件操作错误处理

typescript
class FileOperationError extends Error {
  constructor(
    message: string,
    public readonly operation: string,
    public readonly uri: Uri,
    public readonly originalError?: Error
  ) {
    super(message);
    this.name = 'FileOperationError';
  }
}

const safeFileOperation = async <T>(
  operation: () => Promise<T>,
  operationName: string,
  uri: Uri
): Promise<T> => {
  try {
    return await operation();
  } catch (error) {
    throw new FileOperationError(
      `Failed to ${operationName}: ${error.message}`,
      operationName,
      uri,
      error
    );
  }
};

// 使用示例
const safeReadFile = async (uri: Uri): Promise<string> => {
  return safeFileOperation(
    async () => {
      const content = await workspace.fs.readFile(uri);
      return Buffer.from(content).toString('utf8');
    },
    'read file',
    uri
  );
};

性能优化

文件操作缓存

typescript
class FileCache {
  private cache = new Map<string, { content: string; mtime: number }>();
  
  async readFile(uri: Uri): Promise<string> {
    const key = uri.toString();
    const stat = await workspace.fs.stat(uri);
    
    const cached = this.cache.get(key);
    if (cached && cached.mtime >= stat.mtime) {
      return cached.content;
    }
    
    const content = await workspace.fs.readFile(uri);
    const text = Buffer.from(content).toString('utf8');
    
    this.cache.set(key, { content: text, mtime: stat.mtime });
    return text;
  }
  
  invalidate(uri: Uri): void {
    this.cache.delete(uri.toString());
  }
  
  clear(): void {
    this.cache.clear();
  }
}

并发控制

typescript
class ConcurrentFileOperations {
  private semaphore: number;
  private queue: (() => Promise<any>)[] = [];
  private running = 0;
  
  constructor(maxConcurrent = 10) {
    this.semaphore = maxConcurrent;
  }
  
  async execute<T>(operation: () => Promise<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      this.queue.push(async () => {
        try {
          const result = await operation();
          resolve(result);
        } catch (error) {
          reject(error);
        }
      });
      
      this.processQueue();
    });
  }
  
  private async processQueue(): Promise<void> {
    if (this.running >= this.semaphore || this.queue.length === 0) {
      return;
    }
    
    const operation = this.queue.shift();
    if (!operation) return;
    
    this.running++;
    
    try {
      await operation();
    } finally {
      this.running--;
      this.processQueue();
    }
  }
}

相关 API

您的终极 AI 驱动 IDE 学习指南