登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go archive/tar Writer.Flush 怎样在写下一个条目前完成当前条目

来源:17golang原创

时间:2026-10-24 13:20:29 146浏览 收藏

在 Go 的 archive/tar 中,Writer.Flush 的作用很具体:当前文件条目的内容已经按 Header.Size 完整写入后,它负责补齐 tar 块填充。它不会替你补写遗漏的正文,也不是把任意 io.Writer 的缓存强行刷到磁盘。要在写下一个条目前明确结束当前条目,可以显式调用 Flush;如果紧接着调用 WriteHeader 或最后调用 Close,这两个方法也会隐式完成填充。

先把当前条目的三种数据分开

一个 tar 文件条目由头部、文件内容和为了对齐块大小而产生的填充组成。WriteHeader 根据 Header.Size 设置当前条目允许写入的字节数;Write 只能写内容;Flush 处理的是内容结束后的填充。若实际写入量少于 Header.Size,调用 Flush 会返回遗漏字节的错误,而不是静默生成一个短文件。

tar Writer 当前条目由 Header、内容和块填充组成的静态关系图
图1:结构说明图,查看 Header.Size、当前文件内容与块填充的边界;这不是截图或运行证据。

在切换条目前显式调用 Flush

下面的示例把两个文件写入同一个 tar 流,并在第一个文件完成后显式刷新。示例中的注释只说明关键边界,真正决定当前条目是否完成的是 Header.Size 与实际写入量相等。

package main

import (
    "archive/tar"
    "bytes"
    "fmt"
)

func main() {
    var archive bytes.Buffer
    tw := tar.NewWriter(&archive)

    first := []byte("hello tar")
    if err := tw.WriteHeader(&tar.Header{Name: "first.txt", Mode: 0600, Size: int64(len(first))}); err != nil {
        panic(err) // 头部失败时,当前条目不能继续写入
    }
    if _, err := tw.Write(first); err != nil {
        panic(err) // 内容写入失败要立即停止,不把错误交给 Flush 猜测
    }
    if err := tw.Flush(); err != nil {
        panic(err) // 当前内容完整后,Flush 只负责块填充
    }

    second := []byte("next entry")
    if err := tw.WriteHeader(&tar.Header{Name: "second.txt", Mode: 0600, Size: int64(len(second))}); err != nil {
        panic(err) // WriteHeader 会再次隐式 Flush,仍需检查错误
    }
    if _, err := tw.Write(second); err != nil {
        panic(err) // 每个条目的 Size 都要和实际内容匹配
    }
    if err := tw.Close(); err != nil {
        panic(err) // Close 写入归档尾部,错误不能丢弃
    }
    fmt.Println("tar bytes:", archive.Len())
}

这里显式 Flush 的价值是把“当前条目已完成”变成清晰的代码边界。即使删掉这一行,随后执行的 WriteHeader 仍会隐式刷新;但显式调用适合需要在切换前单独处理错误的场景。

Flush 报错时优先检查 Size 和底层 Writer

最常见的错误是 archive/tar: missed writing N bytes。这说明当前条目还欠 N 个字节,通常是 Header.Size 写大了,或者上游读取只拿到部分数据却没有继续写。反过来,写入超过 Size 会在 Write 阶段报错,不应等到 Flush 才判断。

tar Flush 与 Write、WriteHeader、Close 错误关系的静态结构图
图2:关系说明图,查看 Write 的内容边界、Flush 的遗漏检查、WriteHeader 和 Close 的错误传播关系。

底层目标实现了有错误返回的 io.Writer 时,填充写入失败也会让 Flush 返回该错误,并且 Writer 会保留这个错误。之后再调用导出方法通常仍会得到同一个持久错误,所以应在第一次返回错误时停止继续追加条目。

Flush、WriteHeader 和 Close 怎么选

调用适合场景要记住的边界
Flush切换条目前显式完成当前条目的填充当前条目必须已经完整写完
WriteHeader开始下一个条目会先隐式 Flush,前一个条目未完成就返回错误
Close结束整个 tar 流会完成最后条目的填充并写归档尾部

因此,连续写条目时可以只检查每次 WriteHeader 和最后的 Close;如果业务上需要把“条目完成”和“开始下一个条目”分开记录,使用显式 Flush 更直观。无论采用哪种写法,都不要忽略 Write、Flush、WriteHeader 和 Close 的错误。

常见问题

Flush 能不能补齐我少写的文件内容?不能。它只会在当前内容已完整写入后补块填充;少写会返回遗漏字节错误。

每个条目都必须手动 Flush 吗?不必须。下一次 WriteHeader 或最后的 Close 会隐式完成填充;手动 Flush 主要用于清晰划分边界和单独处理错误。

只调用 Flush 不调用 Close 可以吗?不建议。Flush 只处理当前条目的填充,整个 tar 流的结束还需要 Close 写入归档尾部并报告最终错误。

把 Header.Size 看作当前条目的契约,把 Write 看作内容写入,把 Flush 看作块填充边界,再用 Close 收尾,就能避免把“文件内容未写完”和“tar 对齐填充未写出”混为一谈。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>