文章目录
背景
想把自己的一个SpringBoot应用打包成可执行文件。看看Golang是如何做到的。
Go 的 go:embed 功能介绍与打包 JAR 文件示例
go:embed
是 Go 1.16 引入的一个强大功能,它允许在编译时将外部文件或目录嵌入到 Go 程序中。下面介绍如何使用 go:embed
来嵌入 JAR 文件。
1. go:embed 基础介绍
基本特性
- 编译时嵌入文件内容
- 支持嵌入单个文件、多个文件或整个目录
- 支持三种嵌入类型:
string
- 用于文本文件[]byte
- 用于二进制文件embed.FS
- 用于文件系统(支持多个文件/目录)
基本语法
import "embed"
//go:embed filename.txt
var fileContent string
//go:embed image.png
var imageData []byte
//go:embed templates/*
var templatesFS embed.FS
2. 嵌入 JAR 文件示例
假设你有一个需要嵌入的 app.jar
文件,以下是完整示例:
项目结构
project/
├── embedded/
│ └── app.jar # 要嵌入的JAR文件
├── main.go
└── go.mod
代码实现
package main
import (
"embed"
"io"
"log"
"os"
"path/filepath"
)
//go:embed embedded/app.jar
var jarFile []byte
func main() {
// 指定输出路径(可以是临时目录或特定位置)
outputPath := filepath.Join(os.TempDir(), "app.jar")
// 将嵌入的JAR写入文件系统
err := os.WriteFile(outputPath, jarFile, 0755)
if err != nil {
log.Fatalf("Failed to write JAR file: %v", err)
}
log.Printf("JAR file extracted to: %s", outputPath)
// 现在你可以使用这个JAR文件,比如通过exec.Command运行它
// exec.Command("java", "-jar", outputPath)
}
3. 高级用法:嵌入多个文件或目录
如果你有多个 JAR 文件需要嵌入:
//go:embed embedded/*.jar
var jarFiles embed.FS
func main() {
// 列出所有嵌入的JAR文件
entries, err := jarFiles.ReadDir("embedded")
if err != nil {
log.Fatal(err)
}
// 提取所有JAR文件
for _, entry := range entries {
if filepath.Ext(entry.Name()) == ".jar" {
data, err := jarFiles.ReadFile("embedded/" + entry.Name())
if err != nil {
log.Printf("Failed to read %s: %v", entry.Name(), err)
continue
}
outputPath := filepath.Join(os.TempDir(), entry.Name())
err = os.WriteFile(outputPath, data, 0755)
if err != nil {
log.Printf("Failed to write %s: %v", entry.Name(), err)
continue
}
log.Printf("Extracted %s to %s", entry.Name(), outputPath)
}
}
}
4. 使用注意事项
路径规则:
go:embed
路径是相对于包含该指令的Go源文件的- 不能包含
.
或..
等路径组件 - 不能匹配到父目录中的文件
文件大小限制:
- Go 1.16+ 对嵌入文件大小没有硬性限制
- 但非常大的文件可能会增加编译时间和二进制大小
构建约束:
- 嵌入的文件必须在构建时可用
- 如果文件不存在,构建会失败
安全考虑:
- 提取嵌入文件时要注意目标路径的权限
- 考虑使用临时目录并在程序退出时清理
5. 实际应用场景
- 打包Java应用:将依赖的JAR文件嵌入Go程序,然后通过
exec.Command
调用Java运行 - 嵌入式数据库:如SQLite数据库文件
- Web服务器资源:HTML/CSS/JS等前端资源
- 配置文件:默认配置模板
6. 完整示例:运行嵌入的JAR
package main
import (
"embed"
"log"
"os"
"os/exec"
"path/filepath"
)
//go:embed app.jar
var jarFile []byte
func main() {
// 创建临时文件
tmpFile, err := os.CreateTemp("", "embedded-*.jar")
if err != nil {
log.Fatal(err)
}
defer os.Remove(tmpFile.Name()) // 程序退出时删除
// 写入JAR内容
if _, err := tmpFile.Write(jarFile); err != nil {
log.Fatal(err)
}
if err := tmpFile.Close(); err != nil {
log.Fatal(err)
}
// 执行Java命令
cmd := exec.Command("java", "-jar", tmpFile.Name())
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Println("Starting Java application...")
if err := cmd.Run(); err != nil {
log.Fatalf("Java application failed: %v", err)
}
}
通过这种方式,你可以将Java应用程序打包到Go二进制文件中,然后通过Go程序来分发和启动它。