Ai
1 Star 0 Fork 0

Exi/GO-Makefile 命令简化工具

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
zip.go 6.90 KB
一键复制 编辑 原始数据 按行查看 历史
Exi 提交于 2025-07-10 05:13 +08:00 . 压缩调整
package cmd
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
"gitee.com/exi-red/maketools/util"
"github.com/spf13/cobra"
"github.com/klauspost/compress/flate"
z "github.com/klauspost/compress/zip"
)
// 子命令 rd
const zip = "zip"
// 参数 需要打包的目录
var zipFlagDir = Params{
Name: "dir",
Shorthand: "d",
Value: "",
Usage: "需要打包的目录",
}
// 参数 zip 生成文件路径
var zipFlagZip = Params{
Name: "zip",
Shorthand: "o",
Value: "",
Usage: "zip 生成文件路径",
}
// 参数 排除后缀
var zipFlagExcludeSuffixes = Params{
Name: "exclude-suffixes",
Shorthand: "e",
Value: "",
Usage: "要排除的后缀列表(如 .log)",
}
// 参数 排除后缀
var zipFlagExcludePatterns = Params{
Name: "exclude-patterns",
Shorthand: "p",
Value: "",
Usage: "要排除的正则表达式列表",
}
// 参数 排除后缀
var zipFlagCompressionLevel = Params{
Name: "compression-level",
Shorthand: "l",
IntValue: 6,
Usage: "压缩级别 (0-9, 0=不压缩, 1=最快, 9=最佳)",
}
// 参数 排除后缀
var zipFlagParallelism = Params{
Name: "parallelism",
Shorthand: "P",
IntValue: 0,
Usage: "并行压缩文件数 (0=自动, 8=使用8个CPU核心)",
}
// 注册子命令 zip
var zipCmd = &cobra.Command{
Use: zip,
Short: `将指定目录打包为 zip 文件`,
Example: fmt.Sprintf(
` %s %s ^
-%s "./dir" ^
-%s "dest/demo.zip|/path/to/demo.zip" ^
-%s ".log,/dir" ^
-%s "_test.go$,/(debug|test)/,(.*).rtf$" ^
-%s 6 ^
-%s 8`,
ROOTNAME,
zip,
zipFlagDir.Shorthand,
zipFlagZip.Shorthand,
zipFlagExcludeSuffixes.Shorthand,
zipFlagExcludePatterns.Shorthand,
zipFlagCompressionLevel.Shorthand,
zipFlagParallelism.Shorthand,
),
Run: func(cmd *cobra.Command, args []string) {
// 处理器执行
err := zipHanlder(cmd)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
},
}
// 初始化
func init() {
// 注册参数
StringP(zipCmd, zipFlagDir)
StringP(zipCmd, zipFlagZip)
StringP(zipCmd, zipFlagExcludeSuffixes)
StringP(zipCmd, zipFlagExcludePatterns)
IntP(zipCmd, zipFlagCompressionLevel)
IntP(zipCmd, zipFlagParallelism)
// 添加到主命令
rootCmd.AddCommand(zipCmd)
}
// zip 打包处理器
func zipHanlder(cmd *cobra.Command) error {
// 要打包的目录
dir, _ := cmd.Flags().GetString(zipFlagDir.Name)
if dir == "" {
return errors.New("source directory not specified")
}
// zip 输出路径
output, _ := cmd.Flags().GetString(zipFlagZip.Name)
if output == "" {
return errors.New("zip output path not specified")
}
// 获取可选参数
suffix, _ := cmd.Flags().GetString(zipFlagExcludeSuffixes.Name)
pattern, _ := cmd.Flags().GetString(zipFlagExcludePatterns.Name)
level, _ := cmd.Flags().GetInt(zipFlagCompressionLevel.Name)
parallelism, _ := cmd.Flags().GetInt(zipFlagParallelism.Name)
// 格式化
dir = strings.TrimSpace(dir)
dir = util.WorkDirPath(dir)
if dir != filepath.Clean(dir) {
return errors.New("the packaging target directory cannot be outside the current directory")
}
// 目录不存在
fileInfo, err := os.Stat(dir)
if os.IsNotExist(err) {
return errors.New("the packaging target directory does not exist")
}
// 不是目录
if !fileInfo.IsDir() {
return errors.New("the packaging target is not a directory")
}
// 输出路径
output = util.PathFormat(output)
// 排除后缀
suffixes := make([]string, 0)
for _, suffix := range strings.Split(suffix, ",") {
suffix = strings.TrimSpace(suffix)
if suffix != "" {
suffixes = append(suffixes, suffix)
}
}
// 按正则排除
patterns := make([]string, 0)
for _, pattern := range strings.Split(pattern, ",") {
pattern = strings.TrimSpace(pattern)
if pattern != "" {
patterns = append(patterns, pattern)
}
}
// 验证压缩级别
if level < 0 || level > 9 {
return fmt.Errorf("invalid compression level %d, must be 0-9", level)
}
// 创建ZIP文件
zipFile, err := os.Create(output)
if err != nil {
return fmt.Errorf("failed to create ZIP file:%v", err)
}
defer func(zipFile *os.File) {
_ = zipFile.Close()
}(zipFile)
// 初始化ZIP写入器
zipWriter := z.NewWriter(zipFile)
defer func(zipWriter *z.Writer) {
_ = zipWriter.Close()
}(zipWriter)
// 配置压缩方法
if level > 0 {
zipWriter.RegisterCompressor(z.Deflate, func(w io.Writer) (io.WriteCloser, error) {
return flate.NewWriter(w, level)
})
}
// 设置并行压缩
if parallelism > 0 {
runtime.GOMAXPROCS(parallelism)
}
// 预编译正则表达式
var excludeRegexps []*regexp.Regexp
for _, pattern := range patterns {
re, err := regexp.Compile(pattern)
if err != nil {
return fmt.Errorf("invalid regular expression%q: %v", pattern, err)
}
excludeRegexps = append(excludeRegexps, re)
}
// 遍历目录
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// 跳过根目录本身
if path == dir {
return nil
}
// 获取相对路径
relPath, err := filepath.Rel(dir, path)
if err != nil {
return err
}
// 检查是否应该排除
if shouldExclude(path, info, suffixes, excludeRegexps) {
if info.IsDir() {
return filepath.SkipDir // 跳过整个目录
}
return nil // 跳过文件
}
// 创建ZIP文件头
header, err := z.FileInfoHeader(info)
if err != nil {
return err
}
// 确保路径使用正斜杠
header.Name = filepath.ToSlash(relPath)
header.Method = z.Deflate // 使用标准 ZIP 压缩算法
// 如果是目录,只需要创建目录头
if info.IsDir() {
header.Name += "/"
_, err = zipWriter.CreateHeader(header)
return err
}
// 添加文件到ZIP
writer, err := zipWriter.CreateHeader(header)
if err != nil {
return err
}
// 打开源文件
file, err := os.Open(path)
if err != nil {
return err
}
defer func(file *os.File) {
_ = file.Close()
}(file)
// 复制文件内容
_, err = io.Copy(writer, file)
return err
})
return nil
}
// shouldExclude 检查文件/目录是否应该被排除
func shouldExclude(path string, info os.FileInfo, suffixes []string, excludeRegexps []*regexp.Regexp) bool {
// 确保路径使用正斜杠
path = filepath.ToSlash(path)
// 检查目录排除
if info.IsDir() {
// 检查后缀排除
lowerPath := strings.ToLower(path)
for _, suffix := range suffixes {
if strings.HasSuffix(lowerPath, strings.ToLower(suffix)) {
return true
}
}
// 检查正则表达式排除
for _, re := range excludeRegexps {
if re.MatchString(path) {
return true
}
}
return false
}
// 检查后缀排除
lowerPath := strings.ToLower(path)
for _, suffix := range suffixes {
if strings.HasSuffix(lowerPath, strings.ToLower(suffix)) {
return true
}
}
// 检查正则表达式排除
for _, re := range excludeRegexps {
if re.MatchString(path) {
return true
}
}
return false
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/exi-red/maketools.git
git@gitee.com:exi-red/maketools.git
exi-red
maketools
GO-Makefile 命令简化工具
v1.0.13

搜索帮助