Golang Zip的解压缩,带图形界面

Go是Google开发的开源编程语言,可并行化,并具有垃圾回收功能的编程语言,它是基于编译、垃圾收集和并发的编程语言。并将其开源并在BSD许可证下发行。现在越来越多的公司基于Golang开发产品,因为它拥有非常强大的网络服务编写能力。它具有以下特点:
  • 强调简单、易学
  • 内存管理和语法简单
  • 快速编译
  • 并发支持
  • 静态类型
  • 部署简单(go install)

它的语法挺简单,写起来也简洁,甚至有点代码洁癖,比如引用库但没使用是编不过的,又比如定义变量但没使用也是编不过的。

学习Golang语言,写了一个在windows下带窗口的解压缩文件的demo。当解压文件包含中文名的时候,需要先导入编码转换包,在控制台执行下面语句下载语言包:

go get golang.org/x/text/encoding

然后是Import:

import (
	"golang.org/x/text/encoding/simplifiedchinese"
)

Go语言官方的 go.text 子标准库已经支持各种编码, 下面是utf8转GBK的函数:

func utf8ToGBK(text string) (string, error) {
	dst := make([]byte, len(text)*2)
	tr := simplifiedchinese.GB18030.NewDecoder()
	nDst, _, err := tr.Transform(dst, []byte(text), true)
	if err != nil {
		return text, err
	}
	return string(dst[:nDst]), nil
}

Golang的图形界面库接触不多,试了几个,比如GoQt、nwui、Walk,最后选用了Walk来实现。
这个库有一点比较独特。它所生成的exe文件只有依赖于manifest才能正常运行,但我没找到go打包此资源的方法,所以是使用rsrc这个工具来嵌入到exe文件中。

安装方法一样:

go get github.com/lxn/walk
go get github.com/akavel/rsrc

引入:

"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"

嵌入manifest的命令很简单:

rsrc –manifest %manifestpath% –o % sysopath%

比如我们要为demo.exe打包一个manifest文件,只要这样做:

rsrc -manifest demo.manifest -o rsrc.syso

我们还可以给这个exe程序加上一个桌面小图标:

rsrc -manifest main.manifest –ico test2.ico -o rsrc.syso

首先来创建一个窗口:

type DecryptWindow struct {
	*walk.MainWindow
	prevFilePath string
}

下面封装一个方法用来创建整个图形界面:

func (window *DecryptWindow) OpenDecryptWindow() {
	var zipEdit, saveZipEdit, unzipEdit, saveUnzipEdit *walk.TextEdit
	var zipBtn, unzipBtn, saveZipBtn, saveUnzipBtn, copyInBtn,
		startUnzipBtn, startZipBtn *walk.PushButton
	pathMW := new(DecryptWindow)
	pathMW.SetMaximizeBox(false) // 禁止最大化
	pathMW.SetMinimizeBox(true)  // 禁止最小化
	pathMW.SetFixedSize(false)   // 固定窗体大小
	err := MainWindow{
		AssignTo: &pathMW.MainWindow,
		Title:    "解压缩文件",
		MinSize:  Size{480, 500}, //窗口大小
		Layout:   HBox{Spacing: 2},
		MenuItems: []MenuItem{
			Menu{
				Text: "帮助",
				Items: []MenuItem{
					Action{
						Text:        "关于LiteDecrypt",
						OnTriggered: pathMW.openHelperAbout,
					},
				},
			},
		},
		Children: []Widget{
			Composite{
				Layout: Grid{Columns: GridColumns, Spacing: 10},
				Children: []Widget{
					VSplitter{
						ColumnSpan: TextEditColumnSpan,
						Children: []Widget{
							TextEdit{
								AssignTo:    &unzipEdit,
								Text:        filePath.file,
								MinSize:     Size{250, 0},
								ToolTipText: "请输入需要解压缩文件的路径",
							},
						},
					},
					VSplitter{
						ColumnSpan: PushButtonColumnSpan,
						Children: []Widget{
							PushButton{
								AssignTo: &unzipBtn,
								Text:     "选择解压文件",
								MinSize:  Size{130, 0},
								OnClicked: func() {
									path, err := pathMW.openFileManager()
									if err != nil {
										fmt.Println(err)
									}
									if len(path) > 0 {
										unzipEdit.SetText(path) //显示解压文件
									}
								},
							},
						},
					},
					VSplitter{
						ColumnSpan: TextEditColumnSpan,
						Children: []Widget{
							TextEdit{
								AssignTo:    &saveUnzipEdit,
								Text:        filePath.save,
								MinSize:     Size{250, 0},
								ToolTipText: "请输入要保存解压文件的路径",
							},
						},
					},
					VSplitter{
						ColumnSpan: PushButtonColumnSpan,
						Children: []Widget{
							PushButton{
								AssignTo: &saveUnzipBtn,
								Text:     "选择保存路径",
								MinSize:  Size{130, 0},
								OnClicked: func() {
									path, err := pathMW.openDirManager()
									if err != nil {
										fmt.Println(err)
									}
									if len(path) > 0 {
										saveUnzipEdit.SetText(path)
									}
								},
							},
						},
					},
					VSplitter{
						ColumnSpan: TextEditColumnSpan,
						Children: []Widget{
							TextEdit{
								AssignTo:    &zipEdit,
								Text:        filePath.file,
								MinSize:     Size{250, 0},
								ToolTipText: "请输入要压缩文件的路径",
							},
						},
					},
					VSplitter{
						ColumnSpan: PushButtonColumnSpan,
						Children: []Widget{
							PushButton{
								AssignTo: &zipBtn,
								Text:     "选择压缩文件",
								MinSize:  Size{130, 0},
								OnClicked: func() {
									path, err := pathMW.openFileManager()
									if err != nil {
										fmt.Println(err)
									}
									if len(path) > 0 {
										var result string = zipEdit.Text()
										if len(result) > 0 {
											zipEdit.SetText(result + ";" + path)
										} else {
											zipEdit.SetText(path)
										}
									}
								},
							},
						},
					},
					VSplitter{
						ColumnSpan: TextEditColumnSpan,
						Children: []Widget{
							TextEdit{
								AssignTo:    &saveZipEdit,
								Text:        filePath.save,
								MinSize:     Size{250, 0},
								ToolTipText: "请输入要保存压缩包的路径",
							},
						},
					},
					VSplitter{
						ColumnSpan: PushButtonColumnSpan,
						Children: []Widget{
							PushButton{
								AssignTo: &saveZipBtn,
								Text:     "选择保存路径",
								MinSize:  Size{130, 0},
								OnClicked: func() {
									path, err := pathMW.openDirManager()
									if err != nil {
										fmt.Println(err)
									}
									if len(path) > 0 {
										saveZipEdit.SetText(path)
									}
								},
							},
						},
					},
					VSplitter{
						ColumnSpan: GridColumns,
						Children: []Widget{
							Label{
								Text: "",
							},
						},
					},
					VSplitter{
						ColumnSpan: GridColumns,
						Children: []Widget{
							Label{
								AssignTo: &progressLable,
								Text:     "",
								//Font:     Font{Bold: true, PointSize: 13},
							},
						},
					},
					VSplitter{
						ColumnSpan: GridColumns,
						Children: []Widget{
							Label{
								AssignTo:           &copyResultLable,
								Row:                1,
								AlwaysConsumeSpace: true,
								Text:               "",
							},
						},
					},
					VSplitter{
						ColumnSpan: GridColumns,
						Children: []Widget{
							PushButton{
								AssignTo: &startUnzipBtn,
								Text:     "开始解压",
								MinSize:  Size{100, 30},
								OnClicked: func() {
									startUnzipBtn.SetEnabled(false)
									filePath.file = unzipEdit.Text()
									filePath.save = saveUnzipEdit.Text()
									finishChan := make(chan bool, 1) //用于接收结束标志
									go pathMW.startToUnZip(&filePath, finishChan)
									go pathMW.showResultMsgBox(true, startUnzipBtn, finishChan)
								},
							},
						},
					},
					VSplitter{
						ColumnSpan: GridColumns,
						Children: []Widget{
							PushButton{
								AssignTo: &startZipBtn,
								Text:     "开始压缩",
								MinSize:  Size{100, 30},
								OnClicked: func() {
									startZipBtn.SetEnabled(false)
									filePath.file = zipEdit.Text()
									filePath.save = saveZipEdit.Text()
									finishChan := make(chan bool, 1) //用于接收结束标志
									go pathMW.startToZip(&filePath, finishChan)
									go pathMW.showResultMsgBox(false, startZipBtn, finishChan)
								},
							},
						},
					},
					VSplitter{
						ColumnSpan: GridColumns,
						Children: []Widget{
							PushButton{
								AssignTo: &copyInBtn,
								Text:     "拷贝文件",
								MinSize:  Size{100, 10},
								OnClicked: func() {
									copyInBtn.SetEnabled(false)
									pathMW.copySelectedFile(unzipEdit.Text())
									copyInBtn.SetEnabled(true)
								},
							},
						},
					},
				},
			},
		},
	}.Create()
	if err != nil {
		fmt.Println(err)
	}
	pathMW.SetX(650)
	pathMW.SetY(300)
	pathMW.Run()
}

它的图形界面长成下面这样子,有点小丑:

打开window选择文件对话框方法如下:

/**
*弹出选文件的框
 */
func (mw *DecryptWindow) openFileManager() (filePath string, ror error) {
	dlg := new(walk.FileDialog)
	dlg.FilePath = mw.prevFilePath
	dlg.Filter = "Text Files(*.*)"
	dlg.Title = "选择文件"

	if ok, err := dlg.ShowOpen(mw); err != nil {
		return filePath, err
	} else if !ok {
		return filePath, nil
	}
	filePath = dlg.FilePath
	return filePath, nil
}

打一个选择文件保存路径的对话框方法如下:

/**
*弹出选文件夹的框
 */
func (mw *DecryptWindow) openDirManager() (dirPath string, ror error) {
	dlg := new(walk.FileDialog)
	dlg.FilePath = mw.prevFilePath
	dlg.Filter = "Text Files(*.zip)"
	dlg.Title = "选择保存路径"

	if ok, err := dlg.ShowBrowseFolder(mw); err != nil {
		return dirPath, err
	} else if !ok {
		return dirPath, nil
	}
	dirPath = dlg.FilePath
	return dirPath, nil
}

下面来看一下解压过程:

/**
*解压指定文件
 */
func (mw *DecryptWindow) startToUnZip(filePath *FilePath, ch chan<- bool) {
	var success bool = false
	defer func() {
		ch <- success
	}()
	reader, err := zip.OpenReader(filePath.file)
	if err != nil {
		return
	}
	defer reader.Close()
	for _, file := range reader.File {
		rc, err := file.Open()
		if err != nil {
			return
		}
		defer rc.Close()
		filename := filePath.save + file.Name
		newname, _ := utf8ToGBK(filename)
		if file.FileInfo().IsDir() { //如果是目录,则获取目录路径,主要是清除非法字符
			newpath := getDir(newname)
			if !util.IsFileExist(newpath) { //文件夹不存在就创建
				err = os.MkdirAll(newpath, 0755)
				fmt.Println("make a dir:" + newpath)
				if err != nil {
					fmt.Println(err)
					return
				}
			}
		}
		if !file.FileInfo().IsDir() {
			w, err := os.Create(newname)
			fmt.Println("create a file:" + newname)
			if err != nil {
				return
			}
			defer w.Close()
			_, err = io.Copy(w, rc)
			if err != nil {
				return
			}
			w.Close()
		}
		rc.Close()
	}
	success = true
}

func utf8ToGBK(text string) (string, error) {
	dst := make([]byte, len(text)*2)
	tr := simplifiedchinese.GB18030.NewDecoder()
	nDst, _, err := tr.Transform(dst, []byte(text), true)
	if err != nil {
		return text, err
	}
	return string(dst[:nDst]), nil
}

/**
*获取目录
 */
func getDir(path string) string {
	var index = strings.LastIndex(path, "/")
	var check bool = true
	if index == -1 {
		check = false
		index = strings.LastIndex(path, "\\")
		if index == -1 {
			return path
		}
	}
	var result = util.SubString(path, 0, index)
	for {
		var sep string = "/"
		if !check {
			sep = "\\"
		}
		if strings.HasSuffix(result, sep) {
			index--
			result = util.SubString(result, 0, index)
			fmt.Println("-> result:" + result)
		} else {
			break
		}
	}
	return result
}

具体实现的demo我已经放到github上,欢迎去看看。
Golang解压缩小工具地址

文章目录
|