Documentation
¶
Overview ¶
Package pack 提供跨宿主迁移用的资产包容器:zip 容器 + 一份清单(manifest)。
容器格式 ¶
一个迁移包是标准 zip 文件,包内固定含一份清单 manifest.json:
manifest.json 清单(格式版本、创建时间、各文件行数、各文件 SHA-256 摘要) <其它条目> 数据文件,一行一条 JSON(JSONL)
选 zip 而不是自定义二进制格式,是因为迁移包要能被人在没装本程序的机器上打开看看里面是什么。 archive/zip 是标准库,但「容器 + 清单 + 每文件摘要 + 格式版本 + 体检」这一整套若每个要导出 资产的仓各写一遍,就会出现格式漂移;本包把这套约定固化下来。
写 ¶
w := pack.NewWriter(f, pack.CurrentFormatVersion)
n, err := w.AddJSONL("users.jsonl", rows)
m, err := w.Close() // 清单落盘并返回
清单里的 created_at 取自 Writer 的时刻来源,缺省 time.Now;需要确定性导出 (单测、逐字节可复现的包)时注入固定时钟:
w := pack.NewWriter(f, pack.CurrentFormatVersion, pack.WithClock(func() time.Time {
return fixed
}))
读 ¶
arc, err := pack.Open(r, size)
err = arc.ReadJSONL("users.jsonl", func(row json.RawMessage) error { ... })
体检 ¶
m, problems, err := pack.Inspect(r, size)
Inspect 一次说清所有问题(缺哪几个文件、哪个摘要不符、哪个条数对不上、 哪个条数没核对上),而不是遇到第一个错误就停;err 只在两类「不可导」原因时返回,且可用 errors.Is 区分:
ErrCorrupt 包损坏:zip 读不开、清单缺失/损坏、清单 JSON 解析失败 ErrUnsupportedVersion 版本不认识:清单的 format_version 与调用方期望不符
条数核对覆盖两种键口径:先按文件名精确匹配清单 counts,再按「文件名去扩展名」 的基名宽松匹配(覆盖「键=类别名/基名」的生产者口径)。两种都匹配不上时,Inspect 会明确报出「条数未核对」而不是静默跳过这条检查——体检报「通过」意味着每条 检查都真的跑过了。
rows 支持范围 ¶
AddJSONL 接受「任意可 JSON 编码的行集合」,逐行编码后流式写入,不把整包读进内存:
[]any 元素任意可 JSON 编码(map/string/int/结构体指针……)
[]map[string]any 常见结构
[]struct{...} 结构体切片
[]T 任意元素类型的切片/数组
约束:rows 必须是切片或数组(元素零值也接受);不接受 nil、单个值、map、字节切片 ([]byte 与「行集合」语义冲突,直接报错而不是写出一行 base64)。
摘要 ¶
清单里的摘要是数据文件内容的 SHA-256 十六进制值,与 github.com/oekit/pkg/encoding 的 Sha256Hex 同算法同结果(流式场景用增量 hasher 累积,等价关系由契约测试锁定)。
文件名安全 ¶
写入与读取都做文件名净化:含路径分隔符、含 ".."、绝对路径(含 Windows 盘符)、 控制字符的条目名一律拒绝,读取时记入问题清单——防 zip slip 路径穿越。
Index ¶
Constants ¶
const CurrentFormatVersion = 1
CurrentFormatVersion 是当前写出的清单格式版本。
Variables ¶
var ( // ErrCorrupt 表示迁移包损坏:zip 读不开、清单缺失/损坏、清单 JSON 解析失败。 // 用 errors.Is 判定,报错信息可直接展示给用户。 ErrCorrupt = errors.New("pack: 迁移包损坏") // ErrUnsupportedVersion 表示清单的格式版本不被调用方识别。 // 报错信息里带实际版本号与期望版本号。 ErrUnsupportedVersion = errors.New("pack: 格式版本不兼容") // ErrFileNotFound 表示包内不存在指定文件。 ErrFileNotFound = errors.New("pack: 包内不存在该文件") )
Functions ¶
Types ¶
type Archive ¶
type Archive struct {
// contains filtered or unexported fields
}
Archive 是已打开的迁移包。
func Open ¶
Open 打开迁移包并读取清单。
清单格式版本必须与 CurrentFormatVersion 一致,否则返回包装 ErrUnsupportedVersion 的错误; zip 读不开或清单缺失/损坏时返回包装 ErrCorrupt 的错误。
func OpenVersion ¶
OpenVersion 与 Open 相同,但显式指定调用方期望的清单格式版本。
func (*Archive) ReadJSONL ¶
ReadJSONL 按名读回 JSONL 行,逐行回调。
回调收到的是该行的原始 JSON(json.RawMessage),调用方自行解码; 回调返回错误会中断读取并原样带回(errors.Is 仍可判定回调自己的哨兵错误)。 空行会被跳过。
func (*Archive) ReadJSONLInto ¶
ReadJSONLInto 按名读回 JSONL 行并解码进 dst 指向的切片(追加语义)。
dst 必须是 *[]T 形式(指向切片的非空指针),每行按元素类型解码后追加。
type Manifest ¶
type Manifest struct {
// FormatVersion 是清单格式版本,写入方与读取方必须一致(当前为 CurrentFormatVersion)。
FormatVersion int `json:"format_version"`
// CreatedAt 是包的创建时间(UTC),取自 Writer 的时刻来源
// (缺省 time.Now,可用 WithClock 注入)。
CreatedAt time.Time `json:"created_at"`
// Counts 记录每个数据文件的行数(文件名为键)。
Counts map[string]int `json:"counts"`
// Files 记录每个数据文件的 SHA-256 十六进制摘要(文件名为键)。
Files map[string]string `json:"files"`
}
Manifest 描述迁移包的结构与内容。
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer 逐个写入迁移包的数据文件。
Writer 不是并发安全的;一个包由一个 Writer 顺序写出。
func NewWriter ¶
NewWriter 创建迁移包写入器,底层数据写入 w。
formatVersion 是清单格式版本,应传 CurrentFormatVersion;版本会被写进清单, 读取方据此判断能否识别该包。
可选项 WithClock 注入清单 created_at 的时刻来源(缺省 time.Now)。