我用 Golang 和 go-winres 做了一个文件大搬运

我用 Golang 和 go-winres 做了一个文件大搬运

背景

某天,一位小学教育界一线的朋友找我咨询如何开发一款 windows 应用程序,他说已自学 Python,但在某些细节上不甚清楚。我感叹其学习劲头,有种“编程要从娃娃抓起”的感慨。

经过沟通,他想做一个能将指定文件转移到目标位置的程序,以节省整理资料的时间而更多的将其投入到学生教育中去。我大受感动,说:小学教育界已经这么卷了吗……

为了朋友能更好的完成教育使命,我于是连夜开发了一个简易文件操作程序。该软件被命名为“大搬运”,以纪念搬砖工作者的辛苦付出和各岗位卷王们的不懈努力。

程序设计

  1. 程序主体使用 Golang 开发

  2. 使用 go-winres 进行简易封装(更多使用方法需自行了解)

源码

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync/atomic"
)

var from = flag.String("from", "", "操作目录名,例:c:/a/b")
var to = flag.String("to", "", "目标目录名,例:c:/a/b/c")
var name = flag.String("name", "", "完整文件名或片段")
var mh = flag.Bool("mh", true, "是否开启模糊匹配,默认为 true")
var child = flag.Bool("child", false, "是否开启子目录检查,默认为 false")
var cover = flag.Bool("cover", false, "是否开启同名文件覆盖,关闭覆盖时将自动增补序号,默认为 false")

// main 启动入口
func main() {
	// 解析命令
	flag.Parse()

	// 运行前检查
	beforeCheck()

	// 读取操作目录信息
	fromDir, err := os.ReadDir(*from)
	if err != nil {
		log.Fatalf("检查操作目录失败 err: %s", err.Error())
	}
	// 判断是否需要执行子目录任务
	if *child {
		doWithChild(*from, fromDir)
	} else {
		doCurrent(*from, fromDir)
	}
}



// beforeCheck 前置检查
func beforeCheck() {
	// 操作目录检查
	if *from == "" {
		log.Fatalf("须指定操作目录 from")
	}
	// 目标目录检查
	if *to == "" {
		log.Fatalf("须指定目标目录 to")
	}
	if !isFolderExists(*to) {
		err := os.MkdirAll(*to, os.ModePerm)
		if err != nil {
			log.Fatalf("创建目标目录失败 err: %s", err.Error())
		}
	}
}

// doCurrent 操作当前目录
func doCurrent(dir string, fromDir []os.DirEntry) {
	for _, f := range fromDir {
		// 跳过子目录
		if f.IsDir() {
			continue
		}
		// 操作文件
		moveFile(dir, f)
	}
}

// doWithChild 操作包含子目录
func doWithChild(dir string, fromDir []os.DirEntry) {
	for _, f := range fromDir {
		// 检查子目录
		if f.IsDir() {
			childDir := filepath.Join(dir, f.Name())
			fromChildDir, err := os.ReadDir(childDir)
			if err != nil {
				log.Printf("[ERROR] 读取目录[%s]失败 err: %s", f.Name(), err.Error())
				continue
			}
			// 优先处理子目录
			doWithChild(childDir, fromChildDir)
			continue
		}
		// 操作文件
		moveFile(dir, f)
	}
}

// moveFile 移动文件
func moveFile(dir string, f os.DirEntry) {
	// 仅处理文件,此为双重校验
	if f.IsDir() {
		return
	}
	// 文件名匹配
	fileName := f.Name()
	if *name != "" {
		if *mh {
			if !strings.Contains(fileName, *name) {
				return
			}
		} else {
			if fileName != *name {
				return
			}
		}
	}

	// 移动文件
	srcFilePathName := filepath.Join(dir, fileName)
	srcFile, err := os.Open(srcFilePathName)
	if err != nil {
		log.Printf("[ERROR] 读取文件[%s]失败 err: %s", srcFilePathName, err.Error())
		return
	}
	defer srcFile.Close() // release
	// 创建目标文件
	dstFilePathName := filepath.Join(*to, fileName)
	// 非覆盖模式,需检查序号
	if !(*cover) {
		var index int32
		dirs, err := os.ReadDir(*to)
		if err != nil {
			log.Printf("[ERROR] 读取目标文件[%s]失败 err: %s", *to, err.Error())
			return
		}
		// 检查该目录下文件,确定增补序号
		for _, dir := range dirs {
			if dir.Name() == fileName {
				atomic.AddInt32(&index, 1)
			}
		}

		if index > 0 {
			ns := strings.Split(fileName, ".")
			baseName := ns[0]
			var baseExt string
			if len(ns) > 1 { // for non ext file
				baseExt = ns[1]
			}
			dstFilePathName = filepath.Join(*to, fmt.Sprintf("%s(%d).%s", baseName, index, baseExt))
		}
	}
	// 复制文件并删除原文件
	dstFile, err := os.Create(dstFilePathName)
	if err != nil {
		log.Printf("[ERROR] 创建目标文件[%s]失败 err: %s", dstFilePathName, err.Error())
		return
	}
	defer dstFile.Close() // release
	_, err = io.Copy(dstFile, srcFile)
	if err != nil {
		log.Printf("[ERROR] 复制文件内容[%s]失败 err: %s", srcFilePathName, err.Error())
		return
	}
	err = dstFile.Sync()
	if err != nil {
		log.Printf("[ERROR] 移动文件[%s]失败 err: %s", srcFilePathName, err.Error())
		return
	}
}



// isFolderExists 检查目录是否存在
func isFolderExists(folderName string) bool {
	info, err := os.Stat(folderName)
	if os.IsNotExist(err) {
		return false
	}
	if err != nil {
		return false
	}
	return info.IsDir()
}

编译脚本

  1. 安装 go-winres
go install github.com/tc-hib/go-winres@latest
  1. 初始化 go-winres 配置

进入项目根目录,创建 go-winres 初始化配置,该步骤将创建一个 winres 目录,其中包含两个图片文件和一个名为 winres.json 的配置文件。

go-winres init
  1. 进入 winres 目录视情况修改 winres.json 内配置,常用配置如下。
CompanyName 公司名称
FileDescription 文件描述
FileVersion 文件版本
ProductVersion 程序版本
LegalCopyright 版权信息
OriginalFilename 原始文件名
ProductName 程序名称
  1. 编译资源

在 winres 同级目录下执行编译资源命令

go-winres make
  1. 编译 Golang 程序
go mod tidy

go clean
go env -w CGO_ENABLED=1
SET GOOS=windows
SET GOARCH=amd64
go build -v -ldflags="-w -s" -o bigmove.exe .

操作说明

示例

1. 查看全部命令

命令:bigmove.exe -help

输出:
  -from string
        操作目录名,例:c:/a/b
  -to string
        目标目录名,例:c:/a/b/c
  -name string
        完整文件名或片段
  -mh
        是否开启模糊匹配,默认为 true (default true)
  -child
        是否开启子目录检查,默认为 false
  -cover
        是否开启同名文件覆盖,关闭覆盖时将自动增补序号,默认为 false

2. 将目录 FolderA 下的名为 Name 的文件复制到目录 FolderB,

文件名模糊匹配、增补序号

命令:bigmove.exe -from=FolderA -to=FolderB -name=Name

3. 将目录 FolderA 下的名为 Name 的文件复制到目录 FolderB,

文件名精确匹配、增补序号

命令:bigmove.exe -from=FolderA -to=FolderB -name=Name -mh=false

4. 将目录 FolderA 下的名为 Name 的全部文件复制到目录 FolderB,

子目录检查、文件名模糊匹配、增补序号

命令:bigmove.exe -from=FolderA -to=FolderB -name=Name -child=true

5. 将目录 FolderA 下的名为 Name 的全部文件复制到目录 FolderB,并对已存在文件进行覆盖

子目录检查、文件名模糊匹配、文件覆盖

命令:bigmove.exe -from=FolderA -to=FolderB -name=Name -child=true -cover=true

后来

朋友在软件的帮助下节省了大量整理文件时间,并被告知今年很有可能被评为先进工作者。我们都很高兴。

  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

差点GDP

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值