OpenCode 实践课:5 分钟用 Go 写出一个命令行 HTTP 静态文件服务器

项目介绍

每次写完前端页面想本地预览,用 Python 的 python -m http.server 总觉得不够灵活——没法自定义端口、不想让别人看到目录列表、还想加点简单的日志。今天用 OpenCode 写一个 Go 语言的命令行静态文件服务器,一行命令启动,支持自定义端口、目录浏览开关和访问日志。

  • 技术栈:Go 1.21+,仅用标准库 net/http
  • 最终效果:一个二进制文件,./fileserver -port 3000 -dir ./public 即可启动
  • 代码量:约 100 行

准备工作

  • 安装 Go(1.21 以上版本)
  • 安装 OpenCode(npm i -g @anthropic-ai/opencode 或参考 opencode.ai
  • 确保 OpenCode 已配置好 API Key

实践过程

第一步:初始化项目

我对 OpenCode 说:

> 帮我在当前目录初始化一个 Go 项目,模块名是 fileserver,写一个 main.go,功能是启动一个 HTTP 静态文件服务器,监听 8080 端口,从当前目录提供文件服务。

OpenCode 生成了 go mod init fileserver 和基础代码:

package main

import (
	"log"
	"net/http"
)

func main() {
	port := ":8080"
	dir := "."

	http.Handle("/", http.FileServer(http.Dir(dir)))

	log.Printf("文件服务器已启动,监听 %s,服务目录 %s", port, dir)
	if err := http.ListenAndServe(port, nil); err != nil {
		log.Fatal(err)
	}
}

要点:Go 标准库的 http.FileServer 已经提供了静态文件服务能力,三行核心代码就搞定了。但默认的 http.Dir 会暴露目录列表,后续需要处理。

第二步:添加命令行参数

我对 OpenCode 说:

> 把端口和目录改成命令行参数,用 flag 包,同时增加一个 -browse 参数控制是否显示目录列表。再加个 -host 参数。

OpenCode 理解了需求,改写了 main 函数:

package main

import (
	"flag"
	"log"
	"net/http"
)

func main() {
	port := flag.String("port", "8080", "监听端口")
	host := flag.String("host", "0.0.0.0", "绑定地址")
	dir := flag.String("dir", ".", "静态文件目录")
	browse := flag.Bool("browse", false, "是否启用目录浏览")
	flag.Parse()

	addr := *host + ":" + *port
	log.Printf("文件服务器已启动,监听 %s,服务目录 %s", addr, *dir)
	log.Fatal(http.ListenAndServe(addr, handler(*dir, *browse)))
}

要点:Go 的 flag 包是标准库的一部分,不需要外部依赖。解析后的值是指针,用 * 解引用。

第三步:实现自定义处理器

我对 OpenCode 说:

> 把 handler 实现出来。如果 browse 为 false,用自定义的文件系统包装一层,返回 404 而不是目录列表。同时给每个请求加上访问日志。

OpenCode 生成了完整的 handler 实现,大概 40 行代码。核心思路是:包装 http.FileSystem,在 Open 方法中判断路径是不是目录,如果是且不允许浏览,就返回一个"不存在的文件"错误,这样 http.FileServer 会自动返回 404。

func handler(dir string, browse bool) http.Handler {
	fs := http.FileServer(noDirList{http.Dir(dir), browse})
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL.Path)
		fs.ServeHTTP(w, r)
	})
}

第四步:用 OpenCode 自动测试

我对 OpenCode 说:

> 帮我启动这个服务器,然后用 curl 测试一下,验证根路径能不能访问,目录浏览关闭时是不是返回 404。

OpenCode 启动了服务器后台进程,然后执行了测试命令。输出显示:

  • GET / 返回了 index.html(200 OK)
  • GET /subdir/(无 index.html 的子目录)返回 404 —— 目录浏览已关闭
  • -browse true 时目录列表正常显示

要点:OpenCode 不仅能写代码,还能帮你运行和测试,省去了手动验证的环节。

完整代码

package main

import (
	"flag"
	"log"
	"net/http"
	"os"
)

type noDirList struct {
	fs     http.FileSystem
	browse bool
}

func (n noDirList) Open(path string) (http.File, error) {
	f, err := n.fs.Open(path)
	if err != nil {
		return nil, err
	}
	if n.browse {
		return f, nil
	}
	stat, err := f.Stat()
	if err != nil {
		f.Close()
		return nil, err
	}
	if stat.IsDir() {
		f.Close()
		return nil, os.ErrNotExist
	}
	return f, nil
}

func handler(dir string, browse bool) http.Handler {
	fs := http.FileServer(noDirList{http.Dir(dir), browse})
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL.Path)
		fs.ServeHTTP(w, r)
	})
}

func main() {
	port := flag.String("port", "8080", "监听端口")
	host := flag.String("host", "0.0.0.0", "绑定地址")
	dir := flag.String("dir", ".", "静态文件目录")
	browse := flag.Bool("browse", false, "是否启用目录浏览")
	flag.Parse()

	addr := *host + ":" + *port
	log.Printf("文件服务器已启动 → http://%s", addr)
	log.Printf("服务目录: %s | 目录浏览: %v", *dir, *browse)
	if err := http.ListenAndServe(addr, handler(*dir, *browse)); err != nil {
		log.Fatal(err)
	}
}

小结

这个小项目从想法到可运行,在 OpenCode 的帮助下不到 5 分钟就完成了。实际的编码工作——写 noDirList 包装结构、Open 方法的错误处理逻辑、命令行参数解析——这些原本需要查文档、试错的过程,现在直接用自然语言描述需求,OpenCode 就能生成符合预期的代码。

相比传统开发方式,时间主要节省在:

不需要查文档:Go 标准库的接口签名、http.FileSystem 的定义,OpenCode 全都知道

即时测试验证:写完代码当场运行、当场调试

关注点分离:你把精力放在"想做什么",而不是"怎么写"

如果你还没试过用 AI 编程助手做日常开发,从这个 100 行的小工具开始,是个不错的切入点。