1. 程式人生 > >Go標準庫tar包及Demo

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
}