实现一个 备份小工具(CLI) ,能够将指定的文件压缩备份,具有定时备份功能,能够通过日志记录过程中的信息方便后期查看,cobra参数说明,以下功能说明:
- 文件选择和压缩功能
- 编写一个基本的备份函数,将指定文件夹中的文件打包成
.zip文件。 - 设计一个函数可以选择需要备份的文件。
- 编写一个基本的备份函数,将指定文件夹中的文件打包成
- 定时任务
- 使用 Go 的
time包或者第三方库(如robfig/cron)实现定时任务。
- 使用 Go 的
- 日志
- 调用log包实现日志功能
package main
import (
"archive/zip"
"fmt"
"io"
"log"
"os"
"strings"
"time"
"github.com/spf13/cobra"
)
func main() {
// 创建日志文件
logFile, err := os.OpenFile("backup.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
fmt.Println("无法创建日志文件:", err)
return
}
defer logFile.Close()
// 设置日志输出到文件
log.SetOutput(logFile)
var filePaths string
var filePathsqp []string
var interval int
var rootCmd = &cobra.Command{
Use: "bakapp",
Short: "这是一个备份小工具",
}
var cmdBak = &cobra.Command{
Use: "bak",
Short: "备份文件",
PreRunE: func(cmd *cobra.Command, args []string) error {
if filePaths == "" {
cmd.Help()
os.Exit(0)
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
var err error
if filePathsqp, err = CheckFile(filePaths); err != nil {
log.Println(err)
fmt.Println(err)
return
}
log.Println("开始备份文件")
zipFile(filePathsqp)
log.Println("备份文件完成")
},
}
var cmdIntervalBak = &cobra.Command{
Use: "IntervalBak",
Short: "定时备份文件",
PreRunE: func(cmd *cobra.Command, args []string) error {
if filePaths == "" || interval == 0 {
cmd.Help()
os.Exit(0)
}
if _, err := CheckFile(filePaths); err != nil {
log.Println(err)
return err
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
intervalZipFile(filePaths, interval) // 直接在后台执行间隔备份
log.Println("定时备份已启动")
},
}
cmdBak.Flags().StringVarP(&filePaths, "path", "p", "", "指定文件路径,多个文件用逗号隔开")
cmdIntervalBak.Flags().IntVarP(&interval, "interval", "i", 60, "指定备份间隔,单位为分钟")
cmdIntervalBak.Flags().StringVarP(&filePaths, "path", "p", "", "指定文件路径,多个文件用逗号隔开")
rootCmd.AddCommand(cmdBak)
rootCmd.AddCommand(cmdIntervalBak)
if err := rootCmd.Execute(); err != nil {
fmt.Println("执行命令失败:", err)
os.Exit(1)
}
}
func CheckFile(filePaths string) ([]string, error) {
// 检查文件是否存在
filePathsqp := strings.Split(filePaths, ",")
for _, filePath := range filePathsqp {
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return nil, fmt.Errorf("文件 %s 不存在", filePath)
}
}
return filePathsqp, nil
}
func zipFile(filePaths []string) error {
// 获取当前日期,包含时分秒
now := time.Now()
date := now.Format("20060102150405")
// 创建zip文件
zipFile, err := os.Create(fmt.Sprintf("%s.zip", date))
if err != nil {
return fmt.Errorf("创建zip文件失败: %v", err)
}
defer zipFile.Close()
//创建zip writer
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
// 遍历文件路径
for _, filePath := range filePaths {
// 打开文件
file, err := os.Open(filePath)
if err != nil {
log.Println("打开文件失败:", err)
return fmt.Errorf("打开文件失败: %v", err)
}
defer file.Close()
// 获取文件信息
fileInfo, err := file.Stat()
if err != nil {
log.Println("获取文件信息失败:", err)
return fmt.Errorf("获取文件信息失败: %v", err)
}
// 创建文件头
header, err := zip.FileInfoHeader(fileInfo)
if err != nil {
log.Println("创建文件头失败:", err)
return fmt.Errorf("创建文件头失败: %v", err)
}
// 设置文件头
header.Name = fileInfo.Name()
header.Method = zip.Deflate // 确保使用有效的压缩方法
// 创建写入器
writer, err := zipWriter.CreateHeader(header)
if err != nil {
log.Println("创建写入器失败:", err)
return fmt.Errorf("创建写入器失败: %v", err)
}
// 写入文件
_, err = io.Copy(writer, file)
if err != nil {
log.Println("写入文件失败:", err)
return fmt.Errorf("写入文件失败: %v", err)
}
}
return nil
}
func intervalZipFile(filePaths string, interval int) {
backupInterval := time.Duration(interval) * time.Minute
ticker := time.NewTicker(backupInterval)
for range ticker.C { // 使用 for range 监听 ticker.C
// 执行备份操作
filePathsqp, err := CheckFile(filePaths)
if err != nil {
log.Println("定时备份失败:", err)
fmt.Println("定时备份失败:", err)
continue
}
if err := zipFile(filePathsqp); err != nil {
fmt.Println("备份失败:", err)
log.Println("定时备份失败:", err)
} else {
fmt.Println("备份完成")
log.Println("定时备份完成")
}
}
}