Go標準庫tar包及Demo
tar包實現了tar格式壓縮檔案的存取。本包目標是覆蓋大多數tar的變種,包括GNU和BSD生成的tar檔案。
參考:
http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5
http://www.gnu.org/software/tar/manual/html_node/Standard.html
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html
Constants
const (
// 型別
TypeReg = '0' // 普通檔案
TypeRegA = '\x00' // 普通檔案
TypeLink = '1' // 硬連結
TypeSymlink = '2' // 符號連結
TypeChar = '3' // 字元裝置節點
TypeBlock = '4' // 塊裝置節點
TypeDir = '5' // 目錄
TypeFifo = '6' // 先進先出佇列節點
TypeCont = '7' // 保留位
TypeXHeader = 'x' // 擴充套件頭
TypeXGlobalHeader = 'g' // 全域性擴充套件頭
TypeGNULongName = 'L' // 下一個檔案記錄有個長名字
TypeGNULongLink = 'K' // 下一個檔案記錄指向一個具有長名字的檔案
TypeGNUSparse = 'S' // 稀疏檔案
)
Struct
Header
tar . Header [struct]
Header代表 tar 檔案檔案裡的單個頭。Header型別的某些欄位可能未使用。
type Header struct {
Name string // 記錄頭域的檔名
Mode int64 // 許可權和模式位
Uid int // 所有者的使用者ID
Gid int // 所有者的組ID
Size int64 // 位元組數(長度)
ModTime time.Time // 修改時間
Typeflag byte // 記錄頭的型別
Linkname string // 連結的目標名
Uname string // 所有者的使用者名稱
Gname string // 所有者的組名
Devmajor int64 // 字元裝置或塊裝置的major number
Devminor int64 // 字元裝置或塊裝置的minor number
AccessTime time.Time // 訪問時間
ChangeTime time.Time // 狀態改變時間
Xattrs map[string]string
}
Function:
- FileInfo
func (h *Header) FileInfo() os.FileInfo
FileInfo返回該 Header 對應的檔案資訊。( os.FileInfo 型別)
Reader
tar . Reader [struct]
Reader提供了對一個 tar 檔案檔案的順序讀取。一個 tar 檔案檔案包含一系列檔案。 Next 方法返回檔案中的下一個檔案(包括第一個),返回值可以被視為 io.Reader 來獲取檔案的資料。
type Reader struct {
}
Function:
- Next
- Read
func (tr *Reader) Next() (*Header, error)
轉入 tar 檔案檔案下一記錄,它會返回下一記錄的頭域。
func (tr *Reader) Read(b []byte) (n int, err error)
從檔案檔案的當前記錄讀取資料,到達記錄末端時返回(0,EOF),直到呼叫 Next 方法轉入下一記錄。
Writer
tar.Writer [struct]
Writer型別提供了POSIX.1格式的 tar 檔案檔案的順序寫入。一個 tar 檔案檔案包含一系列檔案。呼叫 WriteHeader 來寫入一個新的檔案,然後呼叫 Write 寫入檔案的資料,該記錄寫入的資料不能超過hdr.Size位元組。
type Writer struct {
}
Function:
- Close
- Flush
- Write
- WriteHeader
func (tw *Writer) Close() error
Close關閉 tar 檔案檔案,會將緩衝中未寫入下層的 io.Writer 介面的資料重新整理到下層。
func (tw *Writer) Flush() error
Flush結束當前檔案的寫入。(可選的)
func (tw *Writer) Write(b []byte) (n int, err error)
Write向 tar 檔案檔案的當前記錄中寫入資料。如果寫入的資料總數超出上一次呼叫 WriteHeader 的引數hdr.Size位元組,返回ErrWriteTooLong錯誤。
func (tw *Writer) WriteHeader(hdr *Header) error
WriteHeader寫入hdr並準備接受檔案內容。如果不是第一次呼叫本方法,會呼叫 Flush 。在 Close 之後呼叫本方法會返回ErrWriteAfterClose。
Function:
- FileInfoHeader
func FileInfoHeader(fi os.FileInfo, link string) (*Header, error)
FileInfoHeader返回一個根據fi填寫了部分欄位的Header。如果fi描述一個符號連結,FileInfoHeader函式將link引數作為連結目標。如果fi描述一個目錄,會在名字後面新增斜槓。因為 os.FileInfo 介面的Name方法只返回它描述的檔案的無路徑名,有可能需要將返回值的Name欄位修改為檔案的完整路徑名。
- NewReader
func NewReader(r io.Reader) *Reader
NewReader建立一個從r讀取的Reader。
- NewWriter
func NewWriter(w io.Writer) *Writer
NewWriter建立一個寫入w的*Writer。
Demo程式碼
package main
// tar包實現了檔案的打包功能,可以將多個檔案或者目錄儲存到單一的.tar壓縮檔案中
// tar本身不具有壓縮功能,只能打包檔案或目錄
import (
"archive/tar"
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"syscall"
)
func main() {
tarDemo()
headerDemo()
tarFile := "tarFile.tar"
untarPath := "unTarDir"
untarFile(tarFile, untarPath)
}
Header資訊讀取
//Header Demo
func headerDemo() {
fileName := "./tarFile.tar"
sfileInfo, err := os.Stat(fileName)
if err != nil {
fmt.Println("get file status Err,", err)
return
}
header, err := tar.FileInfoHeader(sfileInfo, "")
if err != nil {
fmt.Println("get Header Info Err,", err)
return
}
fmt.Println(header.Name)
fmt.Println(header.Mode)
fmt.Println(header.Uid)
fmt.Println(header.Gid)
fmt.Println(header.Size)
fmt.Println(header.ModTime)
fmt.Println(header.Typeflag)
fmt.Println(header.Linkname)
fmt.Println(header.Uname)
fmt.Println(header.Gname)
fmt.Println(header.Devmajor)
fmt.Println(header.Devminor)
fmt.Println(header.AccessTime)
fmt.Println(header.ChangeTime)
fmt.Println(header.Xattrs)
}
打包函式例子
//打包Demo
func tarDemo() {
filetarget := "./tarFile.tar"
filesource := "./filedata"
tarfile, err := os.Create(filetarget)
if err != nil {
// if file is exist then delete file
if err == os.ErrExist {
if err := os.Remove(filetarget); err != nil {
fmt.Println(err)
}
} else {
fmt.Println(err)
}
}
defer tarfile.Close()
tarwriter := tar.NewWriter(tarfile)
sfileInfo, err := os.Stat(filesource)
if err != nil {
fmt.Println(err)
}
if !sfileInfo.IsDir() {
tarFile(filesource, sfileInfo, tarwriter)
} else {
tarFolder(filesource, tarwriter)
}
}
tar 單個檔案
//壓縮單個檔案
func tarFile(filesource string, sfileInfo os.FileInfo, tarwriter *tar.Writer) error {
sfile, err := os.Open(filesource)
if err != nil {
fmt.Println(err)
return err
}
defer sfile.Close()
header, err := tar.FileInfoHeader(sfileInfo, "")
if err != nil {
fmt.Println(err)
return err
}
header.Name = filesource
err = tarwriter.WriteHeader(header)
if err != nil {
fmt.Println(err)
return err
}
if _, err = io.Copy(tarwriter, sfile); err != nil {
fmt.Println(err)
return err
}
return nil
}
tar 一個目錄
//壓縮資料夾
func tarFolder(directory string, tarwriter *tar.Writer) error {
return filepath.Walk(directory, func(targetpath string, file os.FileInfo, err error) error {
//read the file failure
if file == nil {
return err
}
if file.IsDir() {
if directory == targetpath {
return nil
}
header, err := tar.FileInfoHeader(file, "")
if err != nil {
return err
}
header.Name = filepath.Join(directory, strings.TrimPrefix(targetpath, directory))
if err = tarwriter.WriteHeader(header); err != nil {
return err
}
os.Mkdir(strings.TrimPrefix(directory, file.Name()), os.ModeDir)
//如果壓縮的目錄裡面還有目錄,則遞迴壓縮
return tarFolder(targetpath, tarwriter)
}
return tarFile(targetpath, file, tarwriter)
})
}
解壓
//untarFile 解壓
func untarFile(tarFile string, untarPath string) error {
//開啟要解包的檔案,tarFile是要解包的 .tar 檔案的路徑
fr, er := os.Open(tarFile)
if er != nil {
return er
}
defer fr.Close()
// 建立 tar.Reader,準備執行解包操作
tr := tar.NewReader(fr)
//用 tr.Next() 來遍歷包中的檔案,然後將檔案的資料儲存到磁碟中
for hdr, er := tr.Next(); er != io.EOF; hdr, er = tr.Next() {
if er != nil {
return er
}
//先建立目錄
fileName := untarPath + "/" + hdr.Name
dir := path.Dir(fileName)
_, err := os.Stat(dir)
//如果err 為空說明資料夾已經存在,就不用建立
if err != nil {
err = os.MkdirAll(dir, os.ModePerm)
if err != nil {
fmt.Print(err)
return err
}
}
//獲取檔案資訊
fi := hdr.FileInfo()
//建立空檔案,準備寫入解壓後的資料
fw, er := os.Create(fileName)
if er != nil {
return er
}
defer fw.Close()
// 寫入解壓後的資料
_, er = io.Copy(fw, tr)
if er != nil {
return er
}
// 設定檔案許可權
os.Chmod(fileName, fi.Mode().Perm())
}
return nil
}