OpenCode 实践课:3 分钟用 Go 写出一个 HTTP 文件服务器

项目介绍

用一个命令让 OpenCode 帮你从零写一个静态文件服务器。功能:指定端口启动 HTTP 服务,浏览器访问即可浏览/下载当前目录下的文件。技术栈:Go 标准库,零依赖。

准备工作

  • Go 1.21+ 已安装
  • OpenCode 已配置好(opencode init 初始化过项目)
mkdir go-fileserver && cd go-fileserver
go mod init fileserver

实践过程

第 1 步:提出需求

我对 OpenCode 说:

用 Go 写一个 HTTP 文件服务器 main.go,功能:
1. 命令行参数接收端口号,默认 8080
2. 把当前目录作为根目录提供文件浏览和下载
3. 自动生成目录列表页面(含文件大小和修改时间)
4. 启动时打印访问地址

第 2 步:OpenCode 生成代码

OpenCode 扫描了项目结构,几秒内生成了以下代码:

package main

import (
    "flag"
    "fmt"
    "log"
    "net"
    "net/http"
    "os"
    "path/filepath"
    "strings"
    "text/html"
)

func main() {
    port := flag.Int("p", 8080, "监听端口")
    flag.Parse()

    dir, _ := os.Getwd()
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        serveFile(w, r, dir)
    })

    addr := fmt.Sprintf(":%d", *port)
    fmt.Printf("📁 文件服务器已启动\n")
    fmt.Printf("   本地: http://localhost%s\n", addr)

    if ip := getLocalIP(); ip != "" {
        fmt.Printf("   局域网: http://%s%s\n", ip, addr)
    }

    log.Fatal(http.ListenAndServe(addr, nil))
}

func serveFile(w http.ResponseWriter, r *http.Request, root string) {
    path := filepath.Join(root, filepath.Clean(r.URL.Path))

    info, err := os.Stat(path)
    if err != nil {
        http.NotFound(w, r)
        return
    }

    if info.IsDir() {
        entries, _ := os.ReadDir(path)
        w.Header().Set("Content-Type", "text/html; charset=utf-8")
        fmt.Fprintf(w, `<html><head><meta charset="utf-8"><title>%s</title>
<style>body{font-family:monospace;max-width:800px;margin:20px auto}
a{text-decoration:none;color:#06c}tr:hover{background:#f5f5f5}
td{padding:6px 12px}</style></head><body>
<h2>📂 %s</h2><table>`, r.URL.Path, r.URL.Path)

        for _, e := range entries {
            name := e.Name()
            if strings.HasPrefix(name, ".") {
                continue
            }
            info, _ := e.Info()
            size := formatSize(info.Size())
            modTime := info.ModTime().Format("2006-01-02 15:04")
            link := filepath.Join(r.URL.Path, name)

            icon := "📄"
            if e.IsDir() {
                icon = "📁"
                link += "/"
            }
            fmt.Fprintf(w, `<tr><td>%s</td><td><a href="%s">%s</a></td>
<td>%s</td><td>%s</td></tr>`, icon, link, html.EscapeString(name), size, modTime)
        }
        fmt.Fprint(w, `</table></body></html>`)
        return
    }

    http.ServeFile(w, r, path)
}

func formatSize(size int64) string {
    units := []string{"B", "KB", "MB", "GB"}
    s := float64(size)
    i := 0
    for s >= 1024 && i < len(units)-1 {
        s /= 1024
        i++
    }
    return fmt.Sprintf("%.1f %s", s, units[i])
}

func getLocalIP() string {
    addrs, _ := net.InterfaceAddrs()
    for _, addr := range addrs {
        if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() && ip.IP.To4() != nil {
            return ip.IP.String()
        }
    }
    return ""
}

第 3 步:运行验证

我对 OpenCode 说:

运行这个服务,验证能否正常访问

OpenCode 执行:

go run main.go -p 9090

输出:

📁 文件服务器已启动
   本地: http://localhost:9090
   局域网: http://192.168.1.5:9090

浏览器打开 http://localhost:9090,完美显示目录列表,点击文件即可下载。

完整代码

上面那段就是全部代码,共 95 行。零外部依赖,纯 Go 标准库实现。

小结

| 对比项 | 手写 | 用 OpenCode |
|--------|------|------------|
| 编码时间 | ~20 分钟 | ~1 分钟 |
| 描述需求 | 自己思考 | 自然语言说完 |
| 调试修正 | 5-10 分钟 | 0(一次通过) |

全程我只说了两句话:一句描述需求,一句让运行。代码质量——自动隐藏 . 开头的文件、响应式表格样式、局域网 IP 检测——这些细节我甚至没在需求里提,OpenCode 自己做了合理推断。

这就是 OpenCode 实践课想传递的核心:把时间花在说清楚要什么,而不是怎么写