You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Go中向HTML模板增量写入数据:解决SQLite大表格渲染问题

解决SQLite大表格流式渲染输出问题

原代码问题分析

你的代码核心问题是每次循环调用tmpl.Execute都会完整渲染整个模板,这会导致客户端收到重复的HTML结构(比如多个<html>、<table>标签),浏览器无法正确解析;同时没有开启流式输出的响应头和手动刷新机制,服务器会默认缓冲所有内容直到请求结束才发送。

解决方案步骤

要实现实时流式渲染,需要拆分模板结构、设置流式响应头,并逐行输出后主动刷新缓冲区:

1. 拆分模板文件

修改./templates/2d.html,把完整HTML拆分为表头、表体行、尾部三个独立片段:

<!-- 2d.html -->
{{define "header"}}
<!DOCTYPE html>
<html>
<head>
    <title>Mavgo Flight</title>
    <style>
        table { border-collapse: collapse; width: 100%; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f2f2f2; }
    </style>
</head>
<body>
    <table>
        <thead>
            <tr>
                {{range .Cols}}
                <th>{{.}}</th>
                {{end}}
            </tr>
        </thead>
        <tbody>
{{end}}

{{define "row"}}
            <tr>
                {{range .}}
                <td>{{.}}</td>
                {{end}}
            </tr>
{{end}}

{{define "footer"}}
        </tbody>
    </table>
</body>
</html>
{{end}}

2. 修改渲染函数

更新renderHTMLTable函数,先输出表头,再逐行输出表体并实时刷新:

func renderHTMLTable(w http.ResponseWriter, result *sqlx.Rows) {
    // 设置流式响应头,禁用缓存
    w.Header().Set("Content-Type", "text/html; charset=utf-8")
    w.Header().Set("Transfer-Encoding", "chunked")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")

    // 获取列名
    cols, err := result.Columns()
    if err != nil {
        log.Println(err, "renderHTMLTable: failed to get columns")
        http.Error(w, "Failed to fetch columns", http.StatusInternalServerError)
        return
    }

    // 解析模板
    tmpl, err := template.ParseFiles("./templates/2d.html")
    if err != nil {
        log.Println("template failed:", err)
        http.Error(w, "Template load failed", http.StatusInternalServerError)
        return
    }

    // 渲染表头模板
    data := HTMLTable{Cols: cols}
    if err := tmpl.ExecuteTemplate(w, "header", data); err != nil {
        log.Println("render header failed:", err)
        return
    }

    // 强制刷新缓冲区,把表头发送给客户端
    if flusher, ok := w.(http.Flusher); ok {
        flusher.Flush()
    }

    // 逐行读取并渲染表体
    for result.Next() {
        rowData, err := result.SliceScan()
        if err != nil {
            log.Println("scan row failed:", err)
            break
        }

        // 转换为字符串切片
        s := make([]string, len(rowData))
        for i, v := range rowData {
            s[i] = fmt.Sprint(v)
        }

        // 渲染单行模板
        if err := tmpl.ExecuteTemplate(w, "row", s); err != nil {
            log.Println("render row failed:", err)
            break
        }

        // 刷新缓冲区,实时发送当前行
        if flusher, ok := w.(http.Flusher); ok {
            flusher.Flush()
        }

        // 可选:大表格场景可添加微小延迟,避免客户端压力过大
        // time.Sleep(10 * time.Millisecond)
    }

    // 检查遍历过程中的错误
    if err := result.Err(); err != nil {
        log.Println("rows iteration error:", err)
    }

    // 渲染表格尾部,补全HTML结构
    if err := tmpl.ExecuteTemplate(w, "footer", nil); err != nil {
        log.Println("render footer failed:", err)
    }

    // 最后一次刷新
    if flusher, ok := w.(http.Flusher); ok {
        flusher.Flush()
    }
}

// 确保HTMLTable结构体定义正确
type HTMLTable struct {
    Cols []string
}

关键改动说明

  • 响应头配置:启用分块传输编码,禁用缓存,明确告诉浏览器准备接收流式内容。
  • 模板拆分:避免重复输出根HTML标签,保证页面结构合法。
  • 手动Flush:每次渲染后强制服务器发送当前内容到客户端,实现实时显示效果。
  • 错误处理优化:给客户端返回明确错误提示,同时保留日志便于排查问题。

内容的提问来源于stack exchange,提问作者Darian Hickman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 13:55:36