自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

聊聊 Go 如何打包與壓縮文件

開發(fā) 前端
本文展示了如何通過(guò)archive/tar?包實(shí)現(xiàn)文件的打包與解包操作,如何通過(guò)compress/gzip包對(duì)tar包開展進(jìn)一步的壓縮與解壓縮。

文件的打包、壓縮與解壓縮是經(jīng)常會(huì)使用到的功能,我們可以通過(guò) tar、gzip 等工具來(lái)完成這些操作。在 Go 中,標(biāo)準(zhǔn)庫(kù)archive與compress為我們提供了這些能力,通過(guò)本文示例,你會(huì)發(fā)現(xiàn)以 Go 編程的方式生成與處理壓縮打包文件也非常簡(jiǎn)單。

打包和壓縮

在開始代碼之前,我們需要明確打包和壓縮的概念。

  • 打包,又被稱為歸檔,指的是一個(gè)文件或目錄的集合,而這個(gè)集合被存儲(chǔ)在一個(gè)文件中。
  • 壓縮,指的是利用算法將文件進(jìn)行處理,以達(dá)到保留最大文件信息,而讓文件體積變小的目的。

以打包工具 tar 為例,通過(guò)其打出來(lái)的文件通常稱為 tar 包,其文件命名通常以 .tar 結(jié)尾。再通過(guò)其他的壓縮工具對(duì) tar 包進(jìn)行壓縮,例如 gzip 壓縮,則得到通常以 .tar.gz 結(jié)尾命名的壓縮文件(在 tar 中可使用 -z 參數(shù)來(lái)調(diào)用gzip)。

tar 包是文件的集合,其結(jié)構(gòu)也是由數(shù)據(jù)段組成的,每塊數(shù)據(jù)段包含了文件頭(描述文件的元信息)和文件內(nèi)容。

+----------------------------------------+
| Header |
| [name][mode][owner][group][size] ... |
+----------------------------------------+
| Content |
| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|
| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|
+----------------------------------------+
| Header |
| [name][mode][owner][group][size] ... |
+----------------------------------------+
| Content |
| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|
| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|
+----------------------------------------+
| ... |

archive 庫(kù)打包與解包

archive 庫(kù)的中文含義是檔案,它的作用就是歸檔(打包)與拆檔(解包)。其提供兩種方案:tar 與 zip,調(diào)用路徑分別為archive/tar和archive/zip。

我們以 tar 為例,來(lái)展示如何實(shí)現(xiàn)文件的打包與解包。

首先,新建目標(biāo)打包文件為 out.tar,再構(gòu)造一些文件數(shù)據(jù) readme.txt、gopher.txt 和 todo.txt 用于歸檔。

import (
"archive/tar"
...

func main() {
// Create and add some files to the archive.
tarPath := "out.tar"
tarFile, err := os.Create(tarPath)
if err != nil {
log.Fatal(err)
}
defer tarFile.Close()
tw := tar.NewWriter(tarFile)
defer tw.Close()
var files = []struct {
Name, Body string
}{
{"readme.txt", "This archive contains some text files."},
{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
{"todo.txt", "Get animal handling license."},
}
...
}

接著依次構(gòu)建文件頭信息,分別指定了文件名、權(quán)限和大?。啥x更多文件頭字段),再通過(guò)*tar.Writer類型的 tw 變量,按序調(diào)用WriteHeader和Write方法將需要打包的數(shù)據(jù)段(文件頭+文件內(nèi)容)寫入到out.tar文件。

...
for _, file := range files {
hdr := &tar.Header{
Name: file.Name,
Mode: 0600,
Size: int64(len(file.Body)),
}
if err := tw.WriteHeader(hdr); err != nil {
log.Fatal(err)
}
if _, err := tw.Write([]byte(file.Body)); err != nil {
log.Fatal(err)
}
}
}

執(zhí)行以上代碼,將得到打包后的 out.tar 文件,可通過(guò) tar 工具指定 -tvf 參數(shù)查看歸檔信息。

$ tar -tvf out.tar
-rw------- 0 0 0 38 Jan 1 1970 readme.txt
-rw------- 0 0 0 35 Jan 1 1970 gopher.txt
-rw------- 0 0 0 28 Jan 1 1970 todo.txt

可以看到,指定的文件信息(文件名、權(quán)限和大?。┓项A(yù)期,但其他未指定的元信息是有誤的,例如日期(直接給的默認(rèn)值)。

如果通過(guò) tar 工具,我們可以執(zhí)行以下命令來(lái)提取 out.tar 中的文件。

$ tar -xvf out.tar
x readme.txt
x gopher.txt
x todo.txt

但在程序中實(shí)現(xiàn),應(yīng)該怎么做呢?

func main() {
tarPath := "out.tar"
tarFile, err := os.Open(tarPath)
if err != nil {
log.Fatal(err)
}
defer tarFile.Close()
tr := tar.NewReader(tarFile)
for {
hdr, err := tr.Next()
// End of archive
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
fmt.Printf("Contents of %s: ", hdr.Name)
if _, err := io.Copy(os.Stdout, tr); err != nil {
log.Fatal(err)
}
fmt.Println()
}
}

// Output:
Contents of readme.txt: This archive contains some text files.
Contents of gopher.txt: Gopher names:
George
Geoffrey
Gonzo
Contents of todo.txt: Get animal handling license.

首先需要打開 out.tar,并構(gòu)造*tar.Reader類型的 tr 變量。之后,利用tr.Next依次提取每個(gè)數(shù)據(jù)段內(nèi)容,并通過(guò) io.Copy(os.Stdout, tr),將文件內(nèi)容拷貝至標(biāo)準(zhǔn)輸出。直到tr.Next遇到io.EOF,它代表讀取到了歸檔文件末尾,則退出提取。

compress 庫(kù)壓縮與解壓縮

compress 庫(kù)中支持了多種壓縮方案,包括 bzip2、flate、gzip、lzw 和 zlib,調(diào)用路徑為compress/xxx。

我們以常用的 gzip 為例,來(lái)展示壓縮與解壓縮代碼。

如果同樣是上文中的文件數(shù)據(jù) readme.txt、gopher.txt 和 todo.txt,我們想得到 tar 歸檔且被壓縮了的 out.tar.gz 文件,應(yīng)該如何做呢?

package main

import (
"archive/tar"
"compress/gzip"
...
)

func main() {
tarPath := "out.tar.gz"
tarFile, err := os.Create(tarPath)
if err != nil {
log.Fatal(err)
}
defer tarFile.Close()
gz := gzip.NewWriter(tarFile)
defer gz.Close()
tw := tar.NewWriter(gz)
defer tw.Close()
...
}

非常簡(jiǎn)單!只需要將tar.NewWriter(tarFile)改為tar.NewWriter(gz)即可,其中g(shù)z是由gzip.NewWriter(tarFile) 而來(lái)。

我們比較有壓縮與無(wú)壓縮的歸檔 tar 包大小,可以看到文件體積從4.0K壓縮為了224B。

$ ls -alh out.tar out.tar.gz
-rw-r--r-- 1 slp staff 4.0K Jul 3 21:52 out.tar
-rw-r--r-- 1 slp staff 224B Jul 3 21:53 out.tar.gz

同理,如果要解壓并解包 out.tar.gz 文件,應(yīng)該如何做呢?

package main

import (
"archive/tar"
"compress/gzip"
...
)

func main() {
tarPath := "out.tar.gz"
tarFile, err := os.Open(tarPath)
if err != nil {
log.Fatal(err)
}
defer tarFile.Close()
gz, err := gzip.NewReader(tarFile)
if err != nil {
log.Fatal(err)
}
defer gz.Close()
tr := tar.NewReader(gz)
...
}

依然很簡(jiǎn)單!只需要將tar.NewReader(tarFile)改為tar.NewReader(gz)即可,其中g(shù)z是由gzip.NewReader(tarFile) 而來(lái)。

總結(jié)

本文展示了如何通過(guò)archive/tar包實(shí)現(xiàn)文件的打包與解包操作,如何通過(guò)compress/gzip包對(duì)tar包開展進(jìn)一步的壓縮與解壓縮。

在展示compress/gzip使用時(shí),多封裝一層Writer/Reader,即可為tar歸檔文件增加壓縮與解壓縮功能。更棒的是,如果你想切換打包/解包、壓縮/解壓縮策略,僅僅替換掉對(duì)應(yīng)的 Writer/Reader 即可。而這種便利,源于 Go 優(yōu)秀的流式 IO 設(shè)計(jì)。

當(dāng)然,紙上得來(lái)終覺淺,絕知此事要躬行。沒有使用過(guò)archive和compress庫(kù)的讀者,可以嘗試用本文未使用過(guò)的方案,來(lái)試著處理打包壓縮文件。

責(zé)任編輯:武曉燕 來(lái)源: Golang技術(shù)分享
相關(guān)推薦

2018-09-14 16:18:26

Linux壓縮文件應(yīng)用程序

2013-07-18 14:16:14

ZipArchive壓iOS開發(fā)

2011-04-08 09:42:19

Access數(shù)據(jù)庫(kù)壓縮文件

2009-12-25 17:55:05

Linux tar

2009-12-16 16:11:14

Linux壓縮文件

2019-04-26 09:50:21

Linux壓縮文件文件夾

2012-05-10 09:43:28

2009-12-03 14:10:06

PHP壓縮文件夾

2020-11-08 14:37:46

Python壓縮文件代碼

2022-06-22 09:24:30

云原生Go 語(yǔ)言

2023-01-31 08:48:49

Go語(yǔ)言文件

2020-05-07 09:18:57

Linux壓縮文件命令

2010-03-03 13:32:08

Python壓縮文件

2009-04-27 13:46:30

網(wǎng)絡(luò)管理拷貝故障

2011-09-15 10:12:11

ubunturar

2021-07-13 06:44:04

Go語(yǔ)言數(shù)組

2009-06-06 19:07:44

java壓縮文件

2019-10-24 08:52:23

Linux壓縮文件歸檔

2009-11-30 09:31:41

Windows 7文件解壓

2019-02-26 13:39:55

Windows 10壓縮文件夾密碼保護(hù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)