2025年的最后一天,分享我使用go语言开发的电子书转换工具网站

前言

基于 Web 的小说转换工具,支持将 txt 文件转换为 epub、mobi、azw3 等电子书格式

其实这个项目是今年八月做的,一直没有分享。

之前在找txt电子书转换EPUB工具的时候,很多都是有广告的,在GitHub上找到一个go开发的命令行工具:kaf-cli ,挺好用的,不过每次都要输入命令有点麻烦,所以我就用go-gin做成了网站,方便时候。

发在了读书群里,也有很多人在使用。

不过最近这个网站已经让我的服务器不堪重负,只能被迫暂停了。

本文简单分享一下这个网站,作为2025年的收尾吧~

这个项目还催生了 Alpine.js 实现国际化的功能。


本项目是从 ystyle/kaf-cli fork 而来,在保留原有命令行功能的基础上,新增了 Web 可视化界面,提供更便捷的使用体验。

项目地址: https://github.com/Deali-Axy/ebook-generator

主要特性

🌐 Web 功能

  • 📁 文件上传:支持 txt 文件上传,最大 50MB
  • 🔄 格式转换:支持转换为 epub、mobi、azw3 格式
  • 📊 实时进度:通过 SSE 实时查看转换进度
  • 📥 文件下载:转换完成后可下载电子书文件
  • 🗑️ 自动清理:支持手动清理临时文件
  • 📖 API 文档:集成 Swagger UI,方便调试
  • 🎨 可视化界面:简洁易用的 HTML 界面

📚 转换功能

  • 自动识别书名和章节
  • 自动识别字符编码(解决中文乱码)
  • 自定义章节标题识别规则
  • 自定义卷的标题识别规则
  • 自动给章节正文生成加粗居中的标题
  • 段落自动识别和缩进
  • 支持生成 Orly 风格的书籍封面
  • 知轩藏书格式文件名自动提取书名和作者
  • 超快速转换(epub 格式生成 300 章/s 以上速度)

截图

主页面

转换进度和下载

web代码

简单贴一些代码吧,其实我还做了登录注册功能的,不过有bug,前端就没搭配加上去了~

go 复制代码
// main 启动Web服务
func main() {
	// 设置Gin模式
	if os.Getenv("GIN_MODE") == "" {
		gin.SetMode(gin.DebugMode)
	}

	// 初始化服务管理器
	serviceManager, err := initServiceManager()
	if err != nil {
		log.Fatal("初始化服务管理器失败:", err)
	}

	// 启动所有服务
	if err := serviceManager.Start(); err != nil {
		log.Fatal("启动服务失败:", err)
	}

	// 设置优雅关闭
	defer func() {
		if err := serviceManager.Stop(); err != nil {
			log.Printf("停止服务时出错: %v", err)
		}
	}()

	// 初始化Web服务相关组件
	initWebServices(serviceManager)

	// 创建Gin引擎
	r := gin.Default()

	// 初始化SEO服务
	seoService := initSEOService()

	// 添加中间件
	r.Use(middleware.CORS())
	r.Use(middleware.Logger())
	r.Use(middleware.Recovery())
	r.Use(middleware.AddSecurityHeaders())
	r.Use(middleware.AddCacheHeaders())
	r.Use(middleware.SEOMiddleware(seoService))

	// 设置文件上传大小限制 (50MB)
	r.MaxMultipartMemory = 50 << 20

	// API路由组
	api := r.Group("/api")
	{
		// 基础转换功能
		api.POST("/upload", handlers.UploadFile)
		api.POST("/convert", handlers.ConvertBook)
		api.GET("/status/:taskId", handlers.GetTaskStatus)
		api.GET("/download/:fileId", handlers.DownloadFile)
		api.DELETE("/cleanup/:taskId", handlers.CleanupTask)
		api.GET("/events/:taskId", handlers.GetTaskEvents)

		// 用户认证相关路由
		auth := api.Group("/auth")
		{
			auth.POST("/register", handlers.Register)
			auth.POST("/login", handlers.Login)
			auth.GET("/profile", handlers.AuthMiddleware(), handlers.GetProfile)
			auth.PUT("/profile", handlers.AuthMiddleware(), handlers.UpdateProfile)
			auth.POST("/logout", handlers.AuthMiddleware(), handlers.Logout)
			auth.POST("/refresh", handlers.AuthMiddleware(), handlers.RefreshToken)
			auth.PUT("/password", handlers.AuthMiddleware(), handlers.ChangePassword)
		}

		// 转换历史相关路由(需要认证)
		history := api.Group("/history")
		history.Use(handlers.AuthMiddleware())
		{
			history.GET("", handlers.GetHistories)
			history.GET("/stats", handlers.GetHistoryStats)
			history.DELETE("/:id", handlers.DeleteHistory)
		}

		// 转换预设相关路由(需要认证)
		presets := api.Group("/presets")
		presets.Use(handlers.AuthMiddleware())
		{
			presets.POST("", handlers.CreatePreset)
			presets.GET("", handlers.GetPresets)
			presets.GET("/:id", handlers.GetPreset)
			presets.PUT("/:id", handlers.UpdatePreset)
			presets.DELETE("/:id", handlers.DeletePreset)
		}

		// 批量转换相关路由(需要认证)
		batch := api.Group("/batch")
		batch.Use(handlers.AuthMiddleware())
		{
			batch.POST("/convert", handlers.BatchConvert)
		}
	}

	// 集成Swagger文档
	r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

	// 静态文件服务
	r.Static("/static", "./web/static")
	r.StaticFile("/", "./web/static/index.html")
	r.StaticFile("/demo", "./web/static/index.html")

	// SEO相关文件 - 动态生成
	r.GET("/sitemap.xml", handlers.GenerateSitemap)
	r.GET("/robots.txt", handlers.GenerateRobotsTxt)

	// SEO状态监控
	seo := r.Group("/seo")
	{
		seo.GET("/status", handlers.GetSEOStatus)
	}

	// 健康检查接口
	r.GET("/health", func(c *gin.Context) {
		c.JSON(200, gin.H{"status": "ok"})
	})

	// 启动服务
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	log.Printf("服务启动在端口: %s", port)
	log.Printf("Swagger文档地址: http://localhost:%s/swagger/index.html", port)

	if err := r.Run(":" + port); err != nil {
		log.Fatal("启动服务失败:", err)
	}
}

小结

就这样吧,时间不早了,祝大家新年快乐吧~

相关推荐
newerp21 小时前
Go net/http 标准库基础
后端·程序员·go
2651940511264801 天前
01-整体架构与高可用
go
程序员爱钓鱼1 天前
Go if 判断详解
前端·后端·go
名字还没想好☜2 天前
Go 的 unsafe.Pointer 实战:零拷贝 []byte↔string 转换与三条铁律
开发语言·后端·golang·go·unsafe
阿里云云原生2 天前
阿里云联合 Datadog,补齐 Go 可观测性最后短板
云原生·go
可观测性用观测云2 天前
零码改造!Go 语言应用上报观测云完整最佳实践
go
斐波那契数列2 天前
参考react 实现一个golang gui
go
程序员爱钓鱼2 天前
Go 运算符详解
后端·面试·go
angryshan2 天前
goLang配置断点流程
go·bug
名字还没想好☜3 天前
Go 的 sync.Cond 实战:用条件变量做等待/通知,比忙轮询省 CPU
开发语言·数据库·后端·golang·go