Golang 如何解压缩文件
根据维基百科的介绍, 数据压缩 或文件压缩是指在保留原始数据的同时,将特定文件/文件夹/任何数据的大小减小的一种过程。文件大小变小有很多好处,如占用存储空间变少,给其他数据提供更多空间,文件大小减小则传输速度更快以及解压缩文件可以解锁各种其他好处。压缩的文件存储在压缩文件扩展名文件夹中,如“ .zip ”、“ .rar ”、“ .tar.gz ”、“ .arj ”和“ .tgz ”。压缩可将文件大小压缩到最大压缩大小。如果不能再压缩,则大小将保持不变,而不会更小。
在数据压缩完成后,我们通常需要将其解压缩以使其容易访问。此过程称为 解压缩 。从压缩文件中提取普通文件/文件夹的过程称为解压缩。
但是如何解压缩文件?
有各种软件可以为我们执行此类任务;我知道的一个最好的例子是 WinRAR/WinZip。它们能够压缩和解压缩数据文件。但是我们要依赖软件多长时间?它们的大部分功能都需要付费,除了财务方面,作为开发人员,我们是否必须永远依赖外部应用程序来满足我们的需求?或者我们是否可以挖掘自己的方式来满足我们的需求?好的,数据解压缩也可以手动完成。下面讨论使用 GoLang 的一种手动方法。
例子:
情况: 存储在归档文件夹中的文件。该程序将所有文件提取到目标位置。
Operating System: | Windows 10 |
---|---|
Processor: | Intel Core i5-8250U |
Memory: | 8GB DDR4 |
Storage: | 256GB Solid State Drive |
package main
import (
"archive/zip"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
)
func main() {
// Unzip 接受两个参数:
// 第一个参数表示源目录,
// 存在压缩文件的路径
// 第二个参数表示目标目录路径,
// 解压缩后的文件将在其中存储
// 注意,目标文件夹将在pwd中创建,
// 然后将文件解压缩到目标文件夹中。
// Unzip返回2个值:
// 压缩目录中的文件名和错误(如果有)
files, err := Unzip("compression-test.zip", "uncompressed files")
// 如果出现任何错误,
// 则该错误值将被分配给err
// 如果没有错误,则err等于nill
// 条件检查以确保如果出现任何错误,
// 我们将打印错误消息并使用log.Fatal记录错误
if err != nil {
log.Fatal(err)
}
fmt.Println("以下文件已解压缩:\n "+strings.Join(files,"\n"))
// main函数结束
}
// Unzip将解压缩一个zip归档文件,
// 复制所有文件和文件夹
// 在zip文件(参数1)
// 到输出目录(参数2)。
func Unzip(src string, destination string) ([]string, error) {
// 存储任何文件名的变量
// 在字符串数组中可用
var filenames []string
// OpenReader将打开Zip文件
// 指定名称并返回一个ReadCloser
// Readcloser关闭Zip文件,
// 使其不可用于I / O
// 它返回两个值:
// 1.对ReadCloser的指针值
// 2.错误消息(如果有)
r, err := zip.OpenReader(src)
// 如果有任何错误则
// (err!= nill)变为真
if err != nil {
// 这个块将会中断循环
// 并返回到此时为止收集的文件名
// 有一个错误消息,
// 然后返回至主函数
return filenames, err
}
defer r.Close()
// 延迟确保文件关闭
// 在程序结束时不管怎样。
for _, f:= range r.File {
// 这个循环将一直运行,
// 直到源目录中有文件为止,
// 并将继续存储文件名,
// 然后进行提取到目标文件夹,直到出现错误为止
// 存储“路径/文件名”以供稍后返回和使用
fpath:= filepath.Join(destination, f.Name)
// 检查是否有任何无效的文件路径
if!strings.HasPrefix(fpath,filepath.Clean(destination)+string(os.PathSeparator)){
return filenames, fmt.Errorf(“%s是非法文件路径”,fpath)
}
// 现在访问的文件名被附加
// 在filenames字符串数组中与其路径
filenames = append(filenames, fpath)
if f.FileInfo().IsDir(){
// 创建一个新文件夹
os.MkdirAll(fpath, os.ModePerm)
继续
}
// 在目标目录中创建文件
如果err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err!= nil {
return filenames, err
}
// 创建的文件将被存储在
// 带有写入和/或截断权限的outFile中
outFile, err:= os.OpenFile(fpath,
os.O_WRONLY | os.O_CREATE | os.O_TRUNC,
f.Mode())
// 如果有任何错误,此块将再次执行
// 过程将返回到main函数
if err!= nil {
// 用现在收集到的文件名
// 和err消息
返回filenames, err
}
rc, err:= f.Open()
// 再次如果有任何错误,
任何错误会在此代码块中执行并返回到主函数
if err != nil {
// 将收集到的文件名和错误信息返回给主函数
return filenames, err
}
_, err = io.Copy(outFile, rc)
// 没有使用defer关闭文件,以便它在循环
// 进入下一个迭代之前关闭outfile。这样
// 在最坏情况下节省了迭代的内存和时间
outFile.Close()
rc.Close()
// 再次,如果有任何错误,会在此代码块中执行并返回到主函数
if err != nil {
// 将收集到的文件名和错误信息返回给主函数
return filenames, err
}
}
// 最后,当每个文件都被附加到文件名的字符串[]中,并且
// 所有文件都被提取到目标目录时,我们将返回文件名
// 和零值nil作为错误值,表示成功执行了该进程
// *只有到达此处才表示成功执行。
return filenames, nil
}
**我的pwd路径:../src/myprograms/unzip和zip**
**源zip文件夹:../src/myprograms/unzip和zip/compression-test.zip**
**Compression-test.zip包含:** _Hello world.txt main.go_
**目标路径:../src/myprograms/unzip和zip/uncompressed files**
**在我的屏幕上输出:**
已解压以下文件:
uncompressed files\compression-test\Hello world.txt
uncompressed files\compression-test\main.go