上个月我写了一个用aardio开发一个更新程序,这次用GO写一次
原理:
获取新版本号
对比版本号
下载更新包
解压更新包
package main
import (
"archive/zip"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"gopkg.in/ini.v1"
)
// 配置结构
type Config struct {
VersionCheckURL string
DownloadURL string
CurrentVersion string
}
// 加载配置
func loadConfig() (*Config, error) {
cfg, err := ini.Load("config.ini")
if err != nil {
return nil, fmt.Errorf("加载配置文件失败: %v", err)
}
return &Config{
VersionCheckURL: cfg.Section("update").Key("VersionCheckURL").String(),
DownloadURL: cfg.Section("update").Key("DownloadURL").String(),
CurrentVersion: cfg.Section("update").Key("CurrentVersion").String(),
}, nil
}
func main() {
// 加载配置
config, err := loadConfig()
if err != nil {
fmt.Printf("配置加载错误: %v\n", err)
return
}
fmt.Println("更新程序启动")
fmt.Printf("当前版本: %s\n", config.CurrentVersion)
// 1. 版本检测
latestVersion, err := checkLatestVersion(config.VersionCheckURL)
if err != nil {
fmt.Printf("版本检测失败: %v\n", err)
return
}
if latestVersion == config.CurrentVersion {
fmt.Println("当前已是最新版本")
return
}
fmt.Printf("发现新版本: %s\n", latestVersion)
// 2. 下载更新
updateFile, err := downloadUpdate(latestVersion, config.DownloadURL)
if err != nil {
fmt.Printf("下载更新失败: %v\n", err)
return
}
defer os.Remove(updateFile)
fmt.Println("下载更新完成")
// 3. 安装更新
if err := installUpdate(updateFile); err != nil {
fmt.Printf("安装更新失败: %v\n", err)
return
}
fmt.Println("更新安装成功")
}
// 检查最新版本
func checkLatestVersion(versionCheckURL string) (string, error) {
resp, err := http.Get(versionCheckURL)
if err != nil {
return "", fmt.Errorf("无法获取版本信息: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("服务器返回错误状态: %s", resp.Status)
}
versionBytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取版本信息失败: %v", err)
}
return string(versionBytes), nil
}
// 下载更新包
func downloadUpdate(version string, baseURL string) (string, error) {
downloadURL := baseURL + "update_" + version + ".zip"
resp, err := http.Get(downloadURL)
if err != nil {
return "", fmt.Errorf("下载失败: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("下载服务器错误: %s", resp.Status)
}
// 创建临时文件保存更新包
tmpFile := filepath.Join(os.TempDir(), "update_"+version+".zip")
out, err := os.Create(tmpFile)
if err != nil {
return "", fmt.Errorf("创建临时文件失败: %v", err)
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
if err != nil {
return "", fmt.Errorf("保存更新文件失败: %v", err)
}
return tmpFile, nil
}
// 安装更新
func installUpdate(updateFile string) error {
// 打开zip文件
r, err := zip.OpenReader(updateFile)
if err != nil {
return fmt.Errorf("打开zip文件失败: %v", err)
}
defer r.Close()
// 获取程序所在目录
destDir := filepath.Dir(os.Args[0])
// 解压所有文件
for _, f := range r.File {
// 创建目标文件路径
destPath := filepath.Join(destDir, f.Name)
// 创建目录结构
if f.FileInfo().IsDir() {
os.MkdirAll(destPath, os.ModePerm)
continue
}
// 打开zip中的文件
rc, err := f.Open()
if err != nil {
return fmt.Errorf("打开zip内文件失败: %v", err)
}
defer rc.Close()
// 创建目标文件
destFile, err := os.Create(destPath)
if err != nil {
return fmt.Errorf("创建目标文件失败: %v", err)
}
defer destFile.Close()
// 复制文件内容
_, err = io.Copy(destFile, rc)
if err != nil {
return fmt.Errorf("写入文件内容失败: %v", err)
}
}
return nil
}
config.ini
[update]
VersionCheckURL = http://127.0.0.1/version.txt
DownloadURL = http://127.0.0.1/updates/
CurrentVersion = 1.0.0