name: Bun文件I/O description: 用于Bun文件I/O:Bun.file、Bun.write、流、目录、全局模式、元数据。 version: 1.0.0
Bun文件I/O
Bun通过Bun.file()和Bun.write()提供快速、优化的文件操作。
读取文件
Bun.file()
// 创建文件引用(惰性,尚未读取)
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(); // ReadableStream
读取特定类型
// 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" };
写入文件
Bun.write()
// 写入字符串
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]));
// 从响应写入
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权限
});
追加
// 使用Bun.file写入器
const file = Bun.file("./log.txt");
const writer = file.writer();
writer.write("Line 1
");
writer.write("Line 2
");
await writer.flush();
writer.end();
// 或使用node:fs
import { appendFile } from "node:fs/promises";
await appendFile("./log.txt", "New line
");
流操作
读取流
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);
}
写入流
const file = Bun.file("./output.txt");
const writer = file.writer();
for await (const chunk of dataSource) {
writer.write(chunk);
}
await writer.end();
管道流
// 文件到文件
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());
},
})
);
目录操作
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(`Dir: ${entry.name}`);
} else if (entry.isFile()) {
console.log(`File: ${entry.name}`);
}
}
// 创建目录
await mkdir("./new-dir", { recursive: true });
// 移除目录
await rmdir("./old-dir", { recursive: true });
// 检查是否存在
const exists = existsSync("./path");
全局模式
// 使用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
文件元数据
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); // 权限
路径操作
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特定
import.meta.dir; // 当前文件所在目录
import.meta.file; // 文件名
import.meta.path; // 完整路径
常见模式
读取JSON配置
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");
复制目录
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));
}
}
}
监视文件
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"]);
临时文件
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, "temporary data");
常见错误
| 错误 | 原因 | 解决方案 |
|---|---|---|
ENOENT |
文件未找到 | 检查路径,使用exists() |
EACCES |
权限被拒绝 | 检查文件权限 |
EISDIR |
是目录 | 对目录使用readdir() |
EEXIST |
已存在 | 对mkdir使用recursive: true |
何时加载参考
加载references/streams-advanced.md当:
- 转换流
- 压缩流
- 二进制协议
加载references/performance.md当:
- 大文件处理
- 内存优化
- 并发操作