文章目錄
- 背景
- Go 的 go:embed 功能介紹與打包 JAR 文件示例
- 1. go:embed 基礎介紹
- 基本特性
- 基本語法
- 2. 嵌入 JAR 文件示例
- 項目結構
- 代碼實現
- 3. 高級用法:嵌入多個文件或目錄
- 4. 使用注意事項
- 5. 實際應用場景
- 6. 完整示例:運行嵌入的JAR
背景
想把自己的一個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 mainimport ("embed""io""log""os""path/filepath"
)//go:embed embedded/app.jar
var jarFile []bytefunc 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.FSfunc 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 mainimport ("embed""log""os""os/exec""path/filepath"
)//go:embed app.jar
var jarFile []bytefunc 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.Stdoutcmd.Stderr = os.Stderrlog.Println("Starting Java application...")if err := cmd.Run(); err != nil {log.Fatalf("Java application failed: %v", err)}
}
通過這種方式,你可以將Java應用程序打包到Go二進制文件中,然后通過Go程序來分發和啟動它。