Skip to content

Bun 文件操作

Bun 提供了高性能的文件 I/O API,比 Node.js 的 fs 模块更快且更易用。本章介绍 Bun 的文件操作方法。

Bun.file() API

创建文件引用

typescript
// 创建文件引用(不会立即读取)
const file = Bun.file("./data.txt");

// 文件信息
console.log(file.name);    // 文件路径
console.log(file.size);    // 文件大小(字节)
console.log(file.type);    // MIME 类型

读取文件内容

typescript
const file = Bun.file("./data.txt");

// 读取为文本
const text = await file.text();
console.log(text);

// 读取为 JSON
const jsonFile = Bun.file("./config.json");
const config = await jsonFile.json();
console.log(config);

// 读取为 ArrayBuffer
const buffer = await file.arrayBuffer();

// 读取为 Blob
const blob = await file.blob();

// 读取为 Uint8Array
const bytes = await file.bytes();

检查文件是否存在

typescript
const file = Bun.file("./maybe-exists.txt");

// 检查文件是否存在
const exists = await file.exists();
console.log(exists ? "文件存在" : "文件不存在");

写入文件

Bun.write()

typescript
// 写入文本
await Bun.write("./output.txt", "Hello, Bun!");

// 写入 JSON
await Bun.write("./data.json", JSON.stringify({ name: "Bun" }, null, 2));

// 写入二进制数据
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
await Bun.write("./binary.bin", bytes);

// 写入 Response 对象
const response = await fetch("https://example.com/image.png");
await Bun.write("./image.png", response);

// 写入 Blob
const blob = new Blob(["Hello World"], { type: "text/plain" });
await Bun.write("./blob.txt", blob);

写入选项

typescript
// 追加模式
const writer = Bun.file("./log.txt").writer();
writer.write("第一行\n");
writer.write("第二行\n");
await writer.end();

流式读写

读取流

typescript
const file = Bun.file("./large-file.txt");

// 获取可读流
const stream = file.stream();

// 使用 for await 逐块读取
for await (const chunk of stream) {
  console.log("读取块:", chunk.length, "字节");
}

写入流

typescript
const file = Bun.file("./output.txt");
const writer = file.writer();

// 写入数据
writer.write("第一行\n");
writer.write("第二行\n");
writer.write(new Uint8Array([65, 66, 67]));

// 完成写入
await writer.end();

管道操作

typescript
// 从一个文件复制到另一个
const source = Bun.file("./source.txt");
const dest = Bun.file("./dest.txt");

await Bun.write(dest, source);

目录操作

读取目录

typescript
import { readdir, readdirSync } from "node:fs/promises";

// 异步读取目录
const files = await readdir("./src");
console.log(files);

// 包含文件类型信息
const entries = await readdir("./src", { withFileTypes: true });
for (const entry of entries) {
  console.log(
    entry.name,
    entry.isDirectory() ? "目录" : "文件"
  );
}

使用 Bun 的 glob

typescript
// 使用 glob 匹配文件
const glob = new Bun.Glob("**/*.ts");

// 遍历匹配的文件
for await (const file of glob.scan("./src")) {
  console.log(file);
}

// 获取所有匹配文件
const files = await Array.fromAsync(glob.scan("./src"));
console.log(files);

创建目录

typescript
import { mkdir } from "node:fs/promises";

// 创建目录
await mkdir("./new-folder");

// 递归创建
await mkdir("./a/b/c", { recursive: true });

删除目录

typescript
import { rmdir, rm } from "node:fs/promises";

// 删除空目录
await rmdir("./empty-folder");

// 递归删除(包含内容)
await rm("./folder-with-content", { recursive: true });

文件信息

获取文件状态

typescript
import { stat, lstat } from "node:fs/promises";

const stats = await stat("./file.txt");

console.log({
  大小: stats.size,
  是文件: stats.isFile(),
  是目录: stats.isDirectory(),
  是符号链接: stats.isSymbolicLink(),
  创建时间: stats.birthtime,
  修改时间: stats.mtime,
  访问时间: stats.atime,
});

使用 Bun.file() 获取信息

typescript
const file = Bun.file("./file.txt");

console.log({
  路径: file.name,
  大小: file.size,
  类型: file.type,
  最后修改: file.lastModified,
});

文件路径操作

path 模块

typescript
import path from "node:path";

// 拼接路径
const fullPath = path.join("./src", "utils", "helper.ts");
// "./src/utils/helper.ts"

// 解析为绝对路径
const absolute = path.resolve("./file.txt");
// "/Users/xxx/project/file.txt"

// 获取目录名
const dir = path.dirname("/path/to/file.txt");
// "/path/to"

// 获取文件名
const base = path.basename("/path/to/file.txt");
// "file.txt"

// 获取扩展名
const ext = path.extname("/path/to/file.txt");
// ".txt"

// 解析路径
const parsed = path.parse("/path/to/file.txt");
// { root: '/', dir: '/path/to', base: 'file.txt', ext: '.txt', name: 'file' }

特殊路径

typescript
// 当前工作目录
console.log(process.cwd());

// 当前文件目录
console.log(import.meta.dir);

// 当前文件路径
console.log(import.meta.path);

// 主模块目录
console.log(Bun.main);

文件复制和移动

复制文件

typescript
import { copyFile } from "node:fs/promises";

// 使用 Node.js API
await copyFile("./source.txt", "./dest.txt");

// 使用 Bun.write
const source = Bun.file("./source.txt");
await Bun.write("./dest.txt", source);

移动/重命名文件

typescript
import { rename } from "node:fs/promises";

// 重命名
await rename("./old-name.txt", "./new-name.txt");

// 移动文件
await rename("./file.txt", "./folder/file.txt");

临时文件

创建临时文件

typescript
import { tmpdir } from "node:os";
import path from "node:path";

// 获取临时目录
const tempDir = tmpdir();

// 创建临时文件
const tempFile = path.join(tempDir, `temp-${Date.now()}.txt`);
await Bun.write(tempFile, "临时内容");

console.log("临时文件:", tempFile);

使用 Bun 临时文件

typescript
// Bun 自动创建临时文件
const tempFile = Bun.file("/tmp/my-temp-file.txt");
await Bun.write(tempFile, "临时数据");

监听文件变化

使用 Bun.watch (实验性)

typescript
import { watch } from "node:fs";

// 监听文件变化
const watcher = watch("./src", { recursive: true }, (event, filename) => {
  console.log(`文件 ${filename} 发生 ${event} 事件`);
});

// 停止监听
// watcher.close();

实际应用

typescript
// 监听配置文件变化并重新加载
import { watch } from "node:fs";

let config = await loadConfig();

watch("./config.json", async (event) => {
  if (event === "change") {
    console.log("配置文件已更改,重新加载...");
    config = await loadConfig();
  }
});

async function loadConfig() {
  return Bun.file("./config.json").json();
}

实际应用示例

日志记录器

typescript
// logger.ts
class Logger {
  private logFile: string;
  private writer: ReturnType<typeof Bun.file.prototype.writer> | null = null;

  constructor(logFile: string) {
    this.logFile = logFile;
  }

  private async getWriter() {
    if (!this.writer) {
      this.writer = Bun.file(this.logFile).writer();
    }
    return this.writer;
  }

  async log(level: string, message: string) {
    const timestamp = new Date().toISOString();
    const line = `[${timestamp}] [${level}] ${message}\n`;
    
    const writer = await this.getWriter();
    writer.write(line);
    
    // 同时输出到控制台
    console.log(line.trim());
  }

  async info(message: string) {
    await this.log("INFO", message);
  }

  async error(message: string) {
    await this.log("ERROR", message);
  }

  async close() {
    if (this.writer) {
      await this.writer.end();
      this.writer = null;
    }
  }
}

// 使用
const logger = new Logger("./app.log");
await logger.info("应用启动");
await logger.error("发生错误");
await logger.close();

文件上传处理

typescript
// upload-server.ts
const server = Bun.serve({
  port: 3000,
  
  async fetch(request) {
    if (request.method === "POST" && request.url.endsWith("/upload")) {
      const formData = await request.formData();
      const file = formData.get("file") as File;
      
      if (!file) {
        return new Response("没有上传文件", { status: 400 });
      }
      
      // 保存文件
      const savePath = `./uploads/${file.name}`;
      await Bun.write(savePath, file);
      
      return Response.json({
        message: "上传成功",
        filename: file.name,
        size: file.size,
      });
    }
    
    return new Response("上传接口: POST /upload", { status: 200 });
  },
});

console.log(`上传服务器运行在 http://localhost:${server.port}`);

配置文件管理

typescript
// config-manager.ts
class ConfigManager<T> {
  private configPath: string;
  private cache: T | null = null;

  constructor(configPath: string) {
    this.configPath = configPath;
  }

  async load(): Promise<T> {
    if (this.cache) return this.cache;
    
    const file = Bun.file(this.configPath);
    if (!(await file.exists())) {
      throw new Error(`配置文件不存在: ${this.configPath}`);
    }
    
    this.cache = await file.json();
    return this.cache!;
  }

  async save(config: T): Promise<void> {
    await Bun.write(
      this.configPath,
      JSON.stringify(config, null, 2)
    );
    this.cache = config;
  }

  async update(updates: Partial<T>): Promise<T> {
    const current = await this.load();
    const updated = { ...current, ...updates };
    await this.save(updated);
    return updated;
  }

  invalidateCache(): void {
    this.cache = null;
  }
}

// 使用
interface AppConfig {
  port: number;
  debug: boolean;
  apiKey: string;
}

const configManager = new ConfigManager<AppConfig>("./config.json");
const config = await configManager.load();
console.log("当前配置:", config);

await configManager.update({ debug: true });

性能对比

Bun 文件 I/O 比 Node.js 快得多:

读取 1MB 文件:
Node.js fs.readFile:  5ms
Bun.file().text():    1ms

写入 1MB 文件:
Node.js fs.writeFile: 10ms
Bun.write():          2ms

小结

本章介绍了:

  • ✅ Bun.file() 和 Bun.write() API
  • ✅ 流式读写大文件
  • ✅ 目录操作和 glob 匹配
  • ✅ 文件信息和路径操作
  • ✅ 文件监听和临时文件
  • ✅ 实际应用示例

下一步

继续阅读 HTTP 服务器 了解如何使用 Bun 创建高性能 Web 服务器。

本站内容仅供学习和研究使用。