epub目录不在content.opf中,需先解压并解析meta-inf/container.xml定位opf,再从中提取nav文档路径;注意命名空间、路径分隔符及navpoint文本嵌套问题。

EPUB 目录结构藏在哪?别直接读 content.opf
EPUB 的目录(NCX 或 Nav Document)不总在 content.opf 里——那是元数据和文件清单,真目录通常独立存在。Golang 解析时若只打开 content.opf 找 toc 属性,大概率扑空。
实际路径得先解压 EPUB(它本质是 ZIP),再查 container.xml 定位根文件,最后按 OPF 中 guide 或 spine 的 toc 属性值去找目标文件。常见错误是硬编码路径如 toc.ncx 或 nav.xhtml,但规范允许自定义名。
-
container.xml一般在META-INF/container.xml,从中提取rootfile的full-path - 用该路径加载 OPF 文件,解析其
metadata下的meta[name="dtb:depth"](旧 NCX)或manifest中item[properties~="nav"](新 Nav Doc) - 优先匹配
properties="nav"的item,fallback 到guide里的reference[type="toc"]
用 go-unarr 还是 archive/zip 解压 EPUB?
archive/zip 足够,go-unarr 是过度设计。EPUB 3.0+ 强制使用 ZIP 标准(DEFLATE 压缩、无加密),archive/zip 全支持,且零依赖、体积小、性能稳。
容易踩的坑是忽略 ZIP 中路径分隔符:Windows 打包的 EPUB 可能用 \,但 Go 的 zip.FileHeader.Name 在所有平台都返回 / 分隔的标准化路径。直接拼接字符串找 META-INF/container.xml 会失败。
立即学习“go语言免费学习笔记(深入)”;
- 遍历
zip.ReadCloser.File时,统一用strings.TrimPrefix(f.FileHeader.Name, "./")清理前缀 - 用
path.Clean()和path.Join()拼路径,别用+或filepath.Join()(后者在 Windows 下可能引入\) - 检查
f.FileHeader.Name是否以META-INF/container.xml结尾,而不是全等匹配(ZIP 工具可能加前缀如epub/META-INF/...)
解析 nav.xhtml 时 XML 命名空间让 encoding/xml 报错?
是的。nav.xhtml 是 XHTML5,根元素带 xmlns="http://www.w3.org/1999/xhtml",而 Go 默认的 encoding/xml 不自动处理默认命名空间,所有标签都会被跳过或解析为空。
解决方法不是换库,而是显式声明命名空间。把结构体字段的 xml: tag 改成 xml:"http://www.w3.org/1999/xhtml html" 这类形式,或者更稳妥地:用 xml.Decoder + 自定义 xml.StartElement 处理逻辑,跳过命名空间校验。
- 最简方案:用
strings.ReplaceAll(htmlBytes, "xmlns=\"http://www.w3.org/1999/xhtml\"", "")预处理内容(仅限可信输入) - 正经方案:定义结构体时写
Nav struct { XMLName xml.Name `xml:"http://www.w3.org/1999/xhtml nav"` ... } - 注意
nav.xhtml中的ol/li/a是嵌套的,别用单层结构体硬解;递归解析比展开所有层级更可靠
为什么从 toc.ncx 提取的标题全是 text 而不是真实文字?
因为 toc.ncx 是 XML,标题文本藏在 navPoint/navLabel/text 的子节点里,不是属性也不是直系文本内容。直接取 Text string `xml:"text"` 会得到空字符串——text 元素本身没内容,它的子 #text 才是。
Golang 的 encoding/xml 对混合内容(元素+文本)支持弱,容易漏掉纯文本节点。别指望一次 Unmarshal 就拿到全部标题。
- 为
navLabel定义字段Text xml.CharData `xml:"text>`,而不是 <code>string - 如果
navPoint下有多个navLabel(多语言场景),需遍历NavLabel []NavLabelItem - 部分 EPUB 用
content元素的src属性定位 HTML 锚点,但标题仍来自text——别误把src当标题
toc 可能在 NCX、Nav Doc、甚至 OPF 的 dc:tableOfContents 里藏三次。别指望一个函数通吃。











