惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - Franky
有赞技术团队
有赞技术团队
博客园 - 司徒正美
量子位
N
News and Events Feed by Topic
T
Threatpost
Last Week in AI
Last Week in AI
D
Darknet – Hacking Tools, Hacker News & Cyber Security
酷 壳 – CoolShell
酷 壳 – CoolShell
C
CERT Recently Published Vulnerability Notes
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
I
Intezer
人人都是产品经理
人人都是产品经理
T
Tenable Blog
IT之家
IT之家
雷峰网
雷峰网
腾讯CDC
博客园 - 聂微东
V
Visual Studio Blog
S
SegmentFault 最新的问题
Scott Helme
Scott Helme
Spread Privacy
Spread Privacy
月光博客
月光博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
V2EX
大猫的无限游戏
大猫的无限游戏
Apple Machine Learning Research
Apple Machine Learning Research
爱范儿
爱范儿
T
Tailwind CSS Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
N
Netflix TechBlog - Medium
J
Java Code Geeks
宝玉的分享
宝玉的分享
F
Full Disclosure
WordPress大学
WordPress大学
A
Arctic Wolf
小众软件
小众软件
AWS News Blog
AWS News Blog
Attack and Defense Labs
Attack and Defense Labs
NISL@THU
NISL@THU
AI
AI
Hugging Face - Blog
Hugging Face - Blog
F
Fortinet All Blogs
云风的 BLOG
云风的 BLOG
N
News | PayPal Newsroom
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org

蛋蛋之家

SUMIFS和LET函数——让 Excel 自己动起来 Halo 博客晚间卡顿排查记:从"玄学卡顿"到"真凶落网" 我用这个开源项目,把 AI 绘画提示词变成了"代码"——awesome-gpt-image-2 深度体验报告 群晖部署 LX Music Server:浏览器就是你的音乐播放器【详细版】 500道Word题库太痛苦?于是我写了个智能学习平台 别用一个人的奔跑,耗尽两个人的余生——《你凭什么认为我会一直喜欢你呢》 博客评论自动填充新方案:兼容 Vue/React 响应式绑定与跨架构填充逻辑 2026 生产级指南:Halo 2.x + PostgreSQL 自动化部署与性能优化手册 从 Typecho 转向 Halo 2.x:全能型 Nginx 架构与 SEO 无损迁移指南 我的 CodeBuddy 装备库:218个技能全公开 + 使用心得 为博客添加自定义评论表情 Comment2Bark:Typecho评论推送到Bark插件 又拍云插件 UpyunFile v1.2 更新:修复 Typecho 1.3.0 兼容性问题 手把手修复RSS 403:从PHP警告到Cloudflare拦截的全链路排查 网站被镜像怎么办?2026年反镜像攻防实战指南 VLESS + Reality + Vision:2026 极致隐蔽节点搭建全解析 给OpenList和Alist加上液态玻璃效果,颜值直接拉满! 网页截图瞬间“赛博飞升”?手把手教你根治 Windows HDR 截图发白 群晖 NAS 影音终极方案:Docker Rclone + OpenList 完美挂载 Plex 全攻略 一个简洁高效的SVG图标库 告别重复劳动!一键填充评论的神奇书签脚本 - 蛋蛋之家 解锁思源笔记的隐藏力量:siyuan-patch 深度体验与技术实现 Oracle Cloud 自救指南:旧手机丢失后如何绕过 MFA 重新登录【更新2026年最新政策】 本站已加入“萌国ICP备案”联萌!😄 Excel 月报自动化指南:告别重复劳动,让数据主动为你服务 梦呓:为异地女友准备的2周年惊喜礼物 Typecho 多吉云插件优化:更灵活的存储目录配置 甲骨文云硬盘扩容指南:从50G到200G的“免费午餐”怎么吃? ⏰ 时间同步:当你的服务器开始“穿越”时,如何优雅地把它拉回现实 Typecho 又拍云文件管理插件修改版使用指南:新增自定义目录结构功能 华为鸿蒙6.0回退4.2记录 最近小公主情绪不佳 劳动节的家乡景色 烟台山公园一游 群晖搭建ChatGPT Web 推荐脚本:ChatGPT - 提示选择器 Activation Method for JetBrains Products (English version) JetBrains 系列产品激活方法 疫情三年后再进校园 修复searchEngineJump在谷歌搜索页面不生效问题 事情既然发生了,可以是遗憾,不要去后悔。 侄女的试唱会比赛 新一代图片编码格式测试 省科技馆之行 Typora Windows版破解补丁及主题推荐 在Typora中使用PicList上传图片 域名ICP备案之变更网站 Ubuntu 系统如何使用 root 用户登录 中国联通——让一切自由连不通 如何选购适合的口罩? 今天移动营业厅遇到一个小姐姐,真的好美 它不乐意 如何解决Typecho文章不是Markdown语法创建问题 探手抓住这一天的尾巴,为自己庆生 收到腾讯云自媒体分享计划2月份礼品 尝试百度Spider抓取异常问题 甲骨文DD重装系统及失联救砖教程 终于MrDoc升级v0.8.7成功了 Windows 端口查询及杀死进程 甲骨文实例开通与X-UI安装 国艺花鸟市场的悠闲 在思源笔记中使用群晖Synology C2对象存储服务 MrDoc升级v0.8.7失败 居居的手工作品 Linux系统时间同步命令小结 AList安装指南 宝塔面板降级 v7.7.0 及解除强制绑定手机、免费安装专业版插件的方法 修复群晖Synology Drive client右键菜单缺失问题 Windows11 展开折叠的右键菜单为传统模式 在 Windows 下批量将 Markdown 文档转为 Word 文档 Typecho的一些修改记录 Markdown语法——Mermaid 有两种新方案—CloudFlare官方免费CNAME接入教程 居居的新冠历程 群辉安装Transmission套件、增强汉化WebUI并备份做种列表 群晖 docker 部署思源笔记 Office for Mac 2019 License Installer 公众号通过api发送小程序消息 Windows 10下 iTunes 备份默认存储路径修改 Excel查找返回多个值,并将返回的值放在同一个单元格里面 七牛云测试域名失效后如何下载空间中的文件 入手三星860 evo 500g SSD固态硬盘 分享一套耐看的鼠标指针 如何重置OmniFocus数据库 Office for Mac 2016 License Installer Mac下配置终端+iTerm 2主题 如何制作苹果系统安装U盘 LNMP部署typecho,开启SSL、强制开启https Stylish样式如何设置为特定网址不生效? Typecho插件错误号:42S01的解决方法 电动车被偷了 热火 R150 上手开箱 UltraMon 3.3.0 汉化+注册机 如何用 MWeb 发布 Typecho 文章 测试 Mweb 发布到印象笔记功能 忘记了iPhone访问限制的密码该怎么办? 新玩具KAREE电子烟,先试试 Mac mini 2011 开启Handoff方法 见微知著 浅谈手游创新 PhoneClean Pro授权注册激活码
Typecho 迁移 Halo 完整教程:数据库视图解决表前缀 + 自动提取封面图 + 去重检测
吴蛋蛋 · 2026-04-14 · via 蛋蛋之家

Typecho迁移Halo完整教程

前言

Typecho 作为经典的 PHP 博客系统,以轻量简洁著称;而 Halo 作为基于 Java 的现代博客平台,在功能丰富度与扩展性上更胜一筹。对我来说 WordPress 过于臃肿、Typecho 过于简陋,而Halo 恰好处于两者的平衡点

这段时间自己本地搭建体验后感觉真的不错,就试着研究如何迁移。然而,官方迁移插件在导入 Typecho 备份时无法识别数据,社区方案也各有缺陷。最终,基于 fghwett/typecho_to_halo 项目进行定制改造,实现了远程图床场景下的完美迁移

所以这篇文章将详细介绍如何将 Typecho 的数据(文章、页面、评论、标签、分类等)完整迁移至 Halo,同时保留远程图床链接并自动提取文章封面图。


方案对比:原项目 vs 本方案

核心差异一览

功能维度原项目本方案适用场景
附件迁移下载至 Halo 附件库禁用(保留原链接)远程图床用户
封面图处理自动提取首图需要视觉优化的博客
适用场景本地附件存储CDN 加速链接保留又拍云/七牛云/阿里云 OSS 用户

为什么禁用附件迁移?

对于使用又拍云、七牛云等远程图床的博客,附件迁移反而会造成困扰:

  1. 存储冗余:远程图片已具备 CDN 加速,无需重复存储至 Halo
  2. 性能损耗:下载再上传的过程浪费迁移时间
  3. 链接失效风险:迁移过程中可能导致图片链接断裂

解决方案:保留原始图床链接,直接享受 CDN 加速。

为什么添加自动提取封面图?

Halo 支持为文章设置封面图(头图),能显著提升博客的视觉层次感。本方案通过正则表达式自动识别文章中的第一张远程图片,将其设为封面,省去手动配置的繁琐。

  • 自动扫描文章内容中的第一张远程图片
  • 将其设置为文章的封面图
  • 无需手动一一设置,提升迁移效率

本版本更新内容

基于之前版本(2026 年 4 月 14 日发布),本版本进一步优化了数据迁移的稳定性和用户体验,主要更新包括:

  1. 数据去重检查:新增 loadExistingData() 函数,迁移前预加载 Halo 已有数据,通过 slug 对比自动跳过已存在的标签、分类、文章和页面,避免重复导入。
  2. 配置完善:更新配置示例,修复目录名称错误,确保与 config-example.yaml 完全一致。
  3. 常见问题更新:针对用户反馈,更新了关于数据重复的解答,明确说明工具已具备去重能力。

技术实现:代码改造详解

改造一:禁用附件迁移

文件apps/migrate/app.go

// 原代码
actions = append(actions, NewAction("迁移附件", app.migrateAttachments))

// 改造后(注释掉该行)
// actions = append(actions, NewAction("迁移附件", app.migrateAttachments))

改造二:新增封面图提取函数

文件internal/halo/post.go

/**
 * extractFirstImage 从文章内容中提取第一张远程图片URL
 * 支持 Markdown 图片语法:![alt](url)
 * 支持 HTML img 标签:<img src="url">
 * 只提取 http:// 或 https:// 开头的远程图片
 */
func extractFirstImage(content string) string {
    // 匹配 Markdown 图片语法
    mdPattern := `!\[.*?\]\((https?://[^\s\)]+\.(?:jpg|jpeg|png|gif|webp|bmp|svg|ico))`
    mdRe := regexp.MustCompile(mdPattern)
    mdMatches := mdRe.FindStringSubmatch(content)
    if len(mdMatches) > 1 {
        return mdMatches[1]
    }
  
    // 匹配 HTML img 标签
    htmlPattern := `<img[^>]+src=["'](https?://[^"']+\.(?:jpg|jpeg|png|gif|webp|bmp|svg|ico))["']`
    htmlRe := regexp.MustCompile(htmlPattern)
    htmlMatches := htmlRe.FindStringSubmatch(content)
    if len(htmlMatches) > 1 {
        return htmlMatches[1]
    }
  
    return ""
}

改造三:集成封面图至文章迁移

文件apps/migrate/app.go - migratePosts 函数内

// 提取文章封面图
cover := extractFirstImage(post.Content)

// 创建文章(新增 cover 参数)
postId, err := h.client.AddPost(halo.Post{
    Title:        post.Title,
    Content:      post.Content,
    Slug:         post.Slug,
    Tags:         tags,
    Categories:   categories,
    Status:       status,
    AllowComment: post.AllowComment,
    Cover:        cover,  // 新增封面图字段
}, h.policyName, h.groupName)

改造四:扩展 Post 结构体

文件internal/halo/post.go

type Post struct {
    Title        string   `json:"title"`
    Content      string   `json:"content"`
    Slug         string   `json:"slug"`
    Tags         []string `json:"tags"`
    Categories   []string `json:"categories"`
    Status       string   `json:"status"`
    AllowComment bool     `json:"allowComment"`
    Cover        string   `json:"cover,omitempty"`  // 新增封面图字段
}

// AddPost 函数内添加封面图设置逻辑
func (c *Client) AddPost(post Post, policyName, groupName string) (string, error) {
    // ... 现有代码 ...
  
    if post.Cover != "" {
        postSetting.Spec.Template = "post"
        postSetting.CustomTemplate = fmt.Sprintf(`{"cover": "%s"}`, post.Cover)
    }
  
    // ... 后续代码 ...
}

迁移工具原理剖析

架构设计

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Typecho DB    │────▶│  typecho_to_halo │────▶│   Halo API      │
│   (MySQL)       │     │   (Go 程序)      │     │   (REST API)    │
└─────────────────┘     └─────────────────┘     └─────────────────┘
        │                        │                       │
        ▼                        ▼                       ▼
   - tc_contents           - 数据读取              - 创建文章
   - tc_metas              - 结构转换              - 创建分类
   - tc_comments           - API 调用              - 创建标签
   - tc_users              - 错误处理              - 创建评论
   - tc_relationships       - 日志输出              - 设置封面图

数据流转过程

阶段操作技术细节
读取GORM 连接 MySQL通过 DSN 连接 Typecho 数据库
转换结构体映射Typecho 表结构 → Halo API 请求体
导入REST API 调用使用 Halo Personal Access Token 认证
优化封面图提取正则匹配文章内容中的远程图片

迁移内容清单

数据类型迁移状态备注
标签 (Tags)✅ 完整支持包含名称、缩略名
分类 (Categories)✅ 完整支持保留层级关系
文章 (Posts)✅ 增强支持自动提取封面图
页面 (Pages)✅ 完整支持独立页面如"关于"、“友链”
评论 (Comments)✅ 完整支持保留评论层级结构
附件 (Attachments)❌ 已禁用远程图床链接保留

环境准备

版本要求

组件版本说明
Typecho1.3.0测试通过版本
Halo2.23.3测试通过版本
Go1.20+如需自行编译

前置检查清单

  • Halo 已安装并正常运行
  • 已获取 Halo Personal Access Token(路径:Halo 后台 → 用户 → 个人令牌 → 生成新令牌)
  • 确认 Typecho 数据库可远程访问
  • 记录 Typecho 表前缀(如 tc_ 或默认的 typecho_

实操步骤

第一步:获取迁移工具

# 克隆仓库(注意是下划线,非连字符)
git clone https://github.com/NoEggEgg/ty2halo.git
cd typecho_to_halo

第二步:配置文件

# 复制示例配置
cp config-example.yaml config.yaml

config.yaml 配置详解

更新说明:配置格式已更新,与 config-example.yaml 保持完全一致。新增 prefix 字段,优化了配置结构。

typecho:
  type: mysql
  # ⚠️ 必须以斜杠结尾!否则附件下载会报错:lookup xxx.comfiles: no such host
  baseUrl: https://your-domain.com/
  dsn: "username:password@tcp(host:port)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
  prefix: typecho_ # 数据库表前缀,默认为 typecho_
  
halo:
  host: your-halo-host:8090
  schema: http        # 或 https
  token: "your-pat-token"
  debug: false
  policyName: default-policy
  groupName: ""

fileManager:
  dir: ./_tmp/        # 临时文件目录

第三步:处理非默认表前缀

问题现象:若 Typecho 使用 tc_ 而非默认的 typecho_ 前缀,运行时会报错 Table 'dbname.typecho_metas' doesn't exist

解决方案:在 MySQL 中创建视图映射

USE your_database_name;

-- 创建视图映射(将 tc_* 映射为 typecho_*)
CREATE OR REPLACE VIEW typecho_metas AS SELECT * FROM tc_metas;
CREATE OR REPLACE VIEW typecho_contents AS SELECT * FROM tc_contents;
CREATE OR REPLACE VIEW typecho_comments AS SELECT * FROM tc_comments;
CREATE OR REPLACE VIEW typecho_users AS SELECT * FROM tc_users;
CREATE OR REPLACE VIEW typecho_options AS SELECT * FROM tc_options;
CREATE OR REPLACE VIEW typecho_fields AS SELECT * FROM tc_fields;
CREATE OR REPLACE VIEW typecho_relationships AS SELECT * FROM tc_relationships;

清理视图(迁移完成后执行):

USE your_database_name;

DROP VIEW IF EXISTS typecho_metas;
DROP VIEW IF EXISTS typecho_contents;
DROP VIEW IF EXISTS typecho_comments;
DROP VIEW IF EXISTS typecho_users;
DROP VIEW IF EXISTS typecho_options;
DROP VIEW IF EXISTS typecho_fields;
DROP VIEW IF EXISTS typecho_relationships;

第四步:执行迁移

方式一:使用预编译版本(推荐)

# Windows
ty2halo.exe

# Linux / macOS
./ty2halo

方式二:自行编译

go mod tidy
go build -o ty2halo.exe

迁移执行顺序

  1. ✅ 迁移标签
  2. ✅ 迁移分类
  3. ❌ 迁移附件(已禁用)
  4. ✅ 迁移文章(自动提取封面图)
  5. ✅ 迁移页面
  6. ✅ 迁移评论

验证与验收

登录 Halo 后台核对数据完整性:

数据类型预期结果验证状态
标签与 Typecho 一致
分类层级结构保留
附件0 个(远程图床保留)
文章111 篇(示例)
页面独立页面完整
评论739 条(示例)

常见问题速查

Q1:重复数据如何处理?

现象:多次运行后标签 / 分类重复。

原因工具未做重复检测。 更新:工具已添加数据去重检查。

解决

  • 在 Halo 后台删除重复数据后重新运行
  • 或修改源码添加存在性检查(需重新编译)
  • 新增:工具已内置数据去重功能,会先查询 Halo 中已存在的数据(通过 slug 对比),自动跳过已存在的标签、分类、文章和页面。但如果手动删除 Halo 数据后重新迁移,仍然会正常导入。

Q2:附件下载报错 lookup xxx.comfiles: no such host

原因baseUrl 缺少末尾斜杠,导致 URL 拼接错误。

解决:确保 baseUrl/ 结尾,如 https://example.com/

Q3:表前缀不匹配

解决:使用上述数据库视图方案,或修改源码中的表名常量。

Q4:如何恢复附件迁移功能?

如需将附件下载至 Halo 本地(不推荐远程图床场景):

// apps/migrate/app.go
// 取消注释以下行
actions = append(actions, NewAction("迁移附件", app.migrateAttachments))

Q5:封面图提取规则是什么?

项目规则
匹配范围仅远程图片(http://https:// 开头)
语法支持Markdown ![alt](url) 与 HTML ![](url)
格式支持jpg, jpeg, png, gif, webp, bmp, svg, ico
优先级取文章中第一张符合条件的图片
手动调整可在 Halo 后台修改封面图

总结

通过定制化的 ty2halo 工具,我们实现了:

优化点效果
禁用附件迁移保留远程图床 CDN 加速,节省存储与时间
自动提取封面图提升博客视觉体验,减少手动配置
数据去重检查新增:自动跳过已存在的数据,避免重复导入,提升迁移稳定性
最小化代码改动基于原项目结构,降低维护成本

后续

这篇文章会持续更新,因为迁移后还有各种其他问题需要调试,比如固定链接等,有点小麻烦。

最后更新:2026 年 4 月 20 日(添加数据去重检查等优化)


参考资源

  • 仓库地址:https://github.com/NoEggEgg/ty2halo
  • 原项目仓库地址:https://github.com/fghwett/typecho_to_halo
  • Halo 官方文档:https://docs.halo.run
  • Typecho 官方网站:https://typecho.org