Skip to content

Bun的静态托管

Posted on:2025年1月3日 at 11:27

index.ts

// index.ts
import type { Serve } from "bun";
import { stat, readdir } from "node:fs/promises";
import ejs from "ejs";

interface Dir {
  url: string;
  type: "dir" | "back" | "file" | "unknown";
  path: string;
  name: string;
}

const BASEURL = "/public"; // 托管项目下public文件夹

const promiseAwait = <T>(promise: Promise<T>) => {
  return promise.then(v => [null, v] as const).catch(e => [e, null] as const);
};

//构建dir目录
const buildDir = async (dir: string, pathname: string) => {
  const arr: Array<Dir> = [];
  const files = await readdir(dir);
  if (pathname !== "/") {
    arr.push({
      url: dir,
      type: "back",
      name: "../",
      path: `javascript:history.back()`,
    });
  }
  for await (const file of files) {
    const url = `${dir}/${file}`;
    const [, where] = await promiseAwait(stat(url));
    if (!where) continue;
    const isDir = where.isDirectory();
    const isFile = where.isFile();
    arr.push({
      url,
      type: isDir ? "dir" : isFile ? "file" : "unknown",
      name: file,
      path: `${pathname === "/" ? "" : pathname}/${file}`,
    });
  }
  return arr;
};

// Bun 托管静态文件
const server: Serve = {
  async fetch(req) {
    const pathname = new URL(req.url).pathname;
    const path = `${process.cwd()}${BASEURL}/${pathname}`;
    // 判断是文件还是目录
    const [error, where] = await promiseAwait(stat(path));
    if (!where) {
      return new Response(error);
    }
    const isDir = where.isDirectory();
    if (isDir) {
      const dirs = await buildDir(path, pathname);
      const html = await ejs.renderFile(`${import.meta.dir}/dir.ejs`, { dirs });
      return new Response(html, {
        headers: {
          "Content-Type": "text/html",
        },
      });
    }
    const isFile = where.isFile();
    if (isFile) {
      const file = Bun.file(path);
      const exists = await file.exists();
      if (exists) return new Response(file);
    }
    return new Response(null, { status: 404 });
  },
};

export default server;

dir.ejs

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Directory</title>
    <style>
      li {
        padding: 6px;
      }
      li.dir::marker {
        content: "📁";
      }

      li.file::marker {
        content: "📄";
      }

      li.back::marker {
        content: "🔙";
      }
    </style>
  </head>
  <body>
    <ul>
      <% for (let i = 0; i < dirs.length; i++) { %> <% const item = dirs[i] %>
      <li class="<%= item.type %>">
        <a href="<%= item.path %>"> <%= item.name %> </a>
      </li>
      <% } %>
    </ul>
  </body>
</html>

usage

bun ./index.ts

example

image image