go练习备份小工具

191 阅读2分钟

实现一个 备份小工具(CLI)  ,能够将指定的文件压缩备份,具有定时备份功能,能够通过日志记录过程中的信息方便后期查看,cobra参数说明,以下功能说明:

  • 文件选择和压缩功能
    • 编写一个基本的备份函数,将指定文件夹中的文件打包成 .zip 文件。
    • 设计一个函数可以选择需要备份的文件。
  • 定时任务
    • 使用 Go 的 time 包或者第三方库(如 robfig/cron)实现定时任务。
  • 日志
    • 调用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("定时备份完成")
		}
	}
}