bun-file-io

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Bun File I/O

Bun 文件I/O操作

Bun provides fast, optimized file operations via
Bun.file()
and
Bun.write()
.
Bun通过
Bun.file()
Bun.write()
提供快速、优化的文件操作。

Reading Files

读取文件

Bun.file()

Bun.file()

typescript
// Create file reference (lazy, doesn't read yet)
const file = Bun.file("./data.txt");

// File properties
console.log(file.size);        // Size in bytes
console.log(file.type);        // MIME type
console.log(file.name);        // File path
console.log(await file.exists()); // Boolean

// Read content
const text = await file.text();
const json = await file.json();
const buffer = await file.arrayBuffer();
const bytes = await file.bytes();   // Uint8Array
const stream = file.stream();       // ReadableStream
typescript
// 创建文件引用(惰性加载,暂不读取)
const file = Bun.file("./data.txt");

// 文件属性
console.log(file.size);        // 字节大小
console.log(file.type);        // MIME类型
console.log(file.name);        // 文件路径
console.log(await file.exists()); // 是否存在(布尔值)

// 读取内容
const text = await file.text();
const json = await file.json();
const buffer = await file.arrayBuffer();
const bytes = await file.bytes();   // Uint8Array类型
const stream = file.stream();       // 可读流

Read Specific Types

读取特定类型文件

typescript
// JSON file
const config = await Bun.file("config.json").json();

// Text file
const content = await Bun.file("readme.md").text();

// Binary file
const binary = await Bun.file("image.png").arrayBuffer();

// With import attributes
import data from "./data.json" with { type: "json" };
import text from "./content.txt" with { type: "text" };
typescript
// JSON文件
const config = await Bun.file("config.json").json();

// 文本文件
const content = await Bun.file("readme.md").text();

// 二进制文件
const binary = await Bun.file("image.png").arrayBuffer();

// 使用导入属性
import data from "./data.json" with { type: "json" };
import text from "./content.txt" with { type: "text" };

Writing Files

写入文件

Bun.write()

Bun.write()

typescript
// Write string
await Bun.write("./output.txt", "Hello World");

// Write JSON
await Bun.write("./data.json", JSON.stringify({ key: "value" }, null, 2));

// Write binary
await Bun.write("./output.bin", new Uint8Array([1, 2, 3]));

// Write from Response
const response = await fetch("https://example.com/image.png");
await Bun.write("./image.png", response);

// Write from another file (efficient copy)
await Bun.write("./copy.txt", Bun.file("./original.txt"));

// Write with options
await Bun.write("./file.txt", "content", {
  mode: 0o644,  // Unix permissions
});
typescript
// 写入字符串
await Bun.write("./output.txt", "Hello World");

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

// 写入二进制数据
await Bun.write("./output.bin", new Uint8Array([1, 2, 3]));

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

// 从另一个文件写入(高效复制)
await Bun.write("./copy.txt", Bun.file("./original.txt"));

// 带选项写入
await Bun.write("./file.txt", "content", {
  mode: 0o644,  // Unix权限
});

Appending

追加内容

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

writer.write("Line 1\n");
writer.write("Line 2\n");
await writer.flush();
writer.end();

// Or use node:fs
import { appendFile } from "node:fs/promises";
await appendFile("./log.txt", "New line\n");
typescript
// 使用Bun.file写入器
const file = Bun.file("./log.txt");
const writer = file.writer();

writer.write("Line 1\n");
writer.write("Line 2\n");
await writer.flush();
writer.end();

// 或使用node:fs
import { appendFile } from "node:fs/promises";
await appendFile("./log.txt", "New line\n");

Streaming

流操作

Read Stream

读取流

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

const reader = stream.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  // Process chunk (Uint8Array)
  console.log(value);
}
typescript
const file = Bun.file("./large-file.txt");
const stream = file.stream();

const reader = stream.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  // 处理数据块(Uint8Array类型)
  console.log(value);
}

Write Stream

写入流

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

for await (const chunk of dataSource) {
  writer.write(chunk);
}

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

for await (const chunk of dataSource) {
  writer.write(chunk);
}

await writer.end();

Pipe Streams

管道流

typescript
// File to file
const input = Bun.file("./input.txt");
const output = Bun.file("./output.txt");
await Bun.write(output, input);

// HTTP response to file
const response = await fetch(url);
await Bun.write("./download.zip", response);

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

const transformed = stream.pipeThrough(
  new TransformStream({
    transform(chunk, controller) {
      // Process chunk
      controller.enqueue(chunk.toUpperCase());
    },
  })
);
typescript
// 文件到文件
const input = Bun.file("./input.txt");
const output = Bun.file("./output.txt");
await Bun.write(output, input);

// HTTP响应到文件
const response = await fetch(url);
await Bun.write("./download.zip", response);

// 处理流
const file = Bun.file("./data.txt");
const stream = file.stream();

const transformed = stream.pipeThrough(
  new TransformStream({
    transform(chunk, controller) {
      // 处理数据块
      controller.enqueue(chunk.toUpperCase());
    },
  })
);

Directory Operations

目录操作

typescript
import { readdir, mkdir, rmdir, stat } from "node:fs/promises";
import { existsSync, mkdirSync } from "node:fs";

// List directory
const files = await readdir("./src");
const filesWithTypes = await readdir("./src", { withFileTypes: true });

for (const entry of filesWithTypes) {
  if (entry.isDirectory()) {
    console.log(`Dir: ${entry.name}`);
  } else if (entry.isFile()) {
    console.log(`File: ${entry.name}`);
  }
}

// Create directory
await mkdir("./new-dir", { recursive: true });

// Remove directory
await rmdir("./old-dir", { recursive: true });

// Check if exists
const exists = existsSync("./path");
typescript
import { readdir, mkdir, rmdir, stat } from "node:fs/promises";
import { existsSync, mkdirSync } from "node:fs";

// 列出目录内容
const files = await readdir("./src");
const filesWithTypes = await readdir("./src", { withFileTypes: true });

for (const entry of filesWithTypes) {
  if (entry.isDirectory()) {
    console.log(`目录: ${entry.name}`);
  } else if (entry.isFile()) {
    console.log(`文件: ${entry.name}`);
  }
}

// 创建目录
await mkdir("./new-dir", { recursive: true });

// 删除目录
await rmdir("./old-dir", { recursive: true });

// 检查是否存在
const exists = existsSync("./path");

Glob Patterns

Glob模式

typescript
// Using Bun.Glob
const glob = new Bun.Glob("**/*.ts");

// Scan directory
for await (const file of glob.scan({ cwd: "./src" })) {
  console.log(file); // Relative paths
}

// Get all matches
const files = await Array.fromAsync(glob.scan("./src"));

// With options
const glob2 = new Bun.Glob("**/*.{ts,tsx}");
for await (const file of glob2.scan({
  cwd: "./src",
  dot: true,           // Include dotfiles
  absolute: true,      // Return absolute paths
  onlyFiles: true,     // Only files, not directories
})) {
  console.log(file);
}

// Test if path matches
const pattern = new Bun.Glob("*.ts");
pattern.match("file.ts");    // true
pattern.match("file.js");    // false
typescript
// 使用Bun.Glob
const glob = new Bun.Glob("**/*.ts");

// 扫描目录
for await (const file of glob.scan({ cwd: "./src" })) {
  console.log(file); // 相对路径
}

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

// 带选项使用
const glob2 = new Bun.Glob("**/*.{ts,tsx}");
for await (const file of glob2.scan({
  cwd: "./src",
  dot: true,           // 包含隐藏文件
  absolute: true,      // 返回绝对路径
  onlyFiles: true,     // 仅返回文件,不包含目录
})) {
  console.log(file);
}

// 测试路径是否匹配
const pattern = new Bun.Glob("*.ts");
pattern.match("file.ts");    // true
pattern.match("file.js");    // false

File Metadata

文件元数据

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

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

console.log(stats.size);          // Size in bytes
console.log(stats.isFile());      // Is regular file
console.log(stats.isDirectory()); // Is directory
console.log(stats.isSymbolicLink()); // Is symlink
console.log(stats.mtime);         // Modified time
console.log(stats.ctime);         // Changed time
console.log(stats.atime);         // Access time
console.log(stats.mode);          // Permissions
typescript
import { stat, lstat } from "node:fs/promises";

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

console.log(stats.size);          // 字节大小
console.log(stats.isFile());      // 是否为普通文件
console.log(stats.isDirectory()); // 是否为目录
console.log(stats.isSymbolicLink()); // 是否为符号链接
console.log(stats.mtime);         // 修改时间
console.log(stats.ctime);         // 变更时间
console.log(stats.atime);         // 访问时间
console.log(stats.mode);          // 权限

Path Operations

路径操作

typescript
import { join, dirname, basename, extname, resolve } from "node:path";

const filePath = "/home/user/project/src/index.ts";

join("a", "b", "c");           // "a/b/c"
dirname(filePath);             // "/home/user/project/src"
basename(filePath);            // "index.ts"
basename(filePath, ".ts");     // "index"
extname(filePath);             // ".ts"
resolve("./relative");         // Absolute path

// Bun-specific
import.meta.dir;   // Directory of current file
import.meta.file;  // Filename
import.meta.path;  // Full path
typescript
import { join, dirname, basename, extname, resolve } from "node:path";

const filePath = "/home/user/project/src/index.ts";

join("a", "b", "c");           // "a/b/c"
dirname(filePath);             // "/home/user/project/src"
basename(filePath);            // "index.ts"
basename(filePath, ".ts");     // "index"
extname(filePath);             // ".ts"
resolve("./relative");         // 绝对路径

// Bun专属API
import.meta.dir;   // 当前文件所在目录
import.meta.file;  // 当前文件名
import.meta.path;  // 当前文件完整路径

Common Patterns

常见使用模式

Read JSON Config

读取JSON配置

typescript
async function loadConfig<T>(path: string): Promise<T> {
  const file = Bun.file(path);
  if (!(await file.exists())) {
    throw new Error(`Config not found: ${path}`);
  }
  return file.json();
}

const config = await loadConfig<AppConfig>("./config.json");
typescript
async function loadConfig<T>(path: string): Promise<T> {
  const file = Bun.file(path);
  if (!(await file.exists())) {
    throw new Error(`配置文件未找到: ${path}`);
  }
  return file.json();
}

const config = await loadConfig<AppConfig>("./config.json");

Copy Directory

复制目录

typescript
import { readdir, mkdir, stat } from "node:fs/promises";
import { join } from "node:path";

async function copyDir(src: string, dest: string) {
  await mkdir(dest, { recursive: true });

  for (const entry of await readdir(src, { withFileTypes: true })) {
    const srcPath = join(src, entry.name);
    const destPath = join(dest, entry.name);

    if (entry.isDirectory()) {
      await copyDir(srcPath, destPath);
    } else {
      await Bun.write(destPath, Bun.file(srcPath));
    }
  }
}
typescript
import { readdir, mkdir, stat } from "node:fs/promises";
import { join } from "node:path";

async function copyDir(src: string, dest: string) {
  await mkdir(dest, { recursive: true });

  for (const entry of await readdir(src, { withFileTypes: true })) {
    const srcPath = join(src, entry.name);
    const destPath = join(dest, entry.name);

    if (entry.isDirectory()) {
      await copyDir(srcPath, destPath);
    } else {
      await Bun.write(destPath, Bun.file(srcPath));
    }
  }
}

Watch Files

监听文件

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

watch("./src", { recursive: true }, (event, filename) => {
  console.log(`${event}: ${filename}`);
});

// Or with Bun's built-in (faster)
const watcher = Bun.spawn(["bun", "--watch", "src/index.ts"]);
typescript
import { watch } from "node:fs";

watch("./src", { recursive: true }, (event, filename) => {
  console.log(`${event}: ${filename}`);
});

// 或使用Bun内置监听(更快)
const watcher = Bun.spawn(["bun", "--watch", "src/index.ts"]);

Temp Files

临时文件

typescript
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

// Create temp directory
const tempDir = await mkdtemp(join(tmpdir(), "app-"));
console.log(tempDir); // /tmp/app-xxxxx

// Write temp file
const tempFile = join(tempDir, "data.txt");
await Bun.write(tempFile, "temporary data");
typescript
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

// 创建临时目录
const tempDir = await mkdtemp(join(tmpdir(), "app-"));
console.log(tempDir); // /tmp/app-xxxxx

// 写入临时文件
const tempFile = join(tempDir, "data.txt");
await Bun.write(tempFile, "临时数据");

Common Errors

常见错误

ErrorCauseFix
ENOENT
File not foundCheck path, use exists()
EACCES
Permission deniedCheck file permissions
EISDIR
Is a directoryUse readdir() for directories
EEXIST
Already existsUse recursive: true for mkdir
错误原因解决方法
ENOENT
文件未找到检查路径,使用exists()方法
EACCES
权限不足检查文件权限
EISDIR
目标是目录对目录使用readdir()方法
EEXIST
文件已存在创建目录时使用recursive: true选项

When to Load References

何时加载参考文档

Load
references/streams-advanced.md
when:
  • Transform streams
  • Compression streams
  • Binary protocols
Load
references/performance.md
when:
  • Large file handling
  • Memory optimization
  • Concurrent operations
当涉及以下内容时,加载
references/streams-advanced.md
  • 转换流
  • 压缩流
  • 二进制协议
当涉及以下内容时,加载
references/performance.md
  • 大文件处理
  • 内存优化
  • 并发操作