Go标准库之Zip

本包不支持跨硬盘的压缩。

关于ZIP64:
为了向下兼容,FileHeader同时拥有32位和64位的Size字段。64位字段总是包含正确的值,对普通格式的档案未见它们的值是相同的。对zip64格式的档案文件32位字段将是0xffffffff,必须使用64位字段。

// Constants
const (
  Store   uint16 = 0
  Deflate uint16 = 8
)
// Struct
    // Header
type File struct {
  FileHeader
}
        // Function
            func (f *File) DataOffset() (offset int64, err error)
            // DataOffset返回文件的可能存在的压缩数据相对于 zip 文件起始的偏移量。大多数调用者应使用 Open 代替,该方法会主动解压缩数据并验证校验和。
            func (f *File) Open() (rc io.ReadCloser, err error)
            // Open方法返回一个 io.ReadCloser 接口,提供读取文件内容的方法。可以同时读取多个文件。

    // FileHeader
type FileHeader struct {
  Name string
  CreatorVersion     uint16
  ReaderVersion      uint16
  Flags              uint16
  Method             uint16
  ModifiedTime       uint16 // MS-DOS时间
  ModifiedDate       uint16 // MS-DOS日期
  CRC32              uint32
  CompressedSize     uint32 // 已弃用;请使用CompressedSize64
  UncompressedSize   uint32 // 已弃用;请使用UncompressedSize64
  CompressedSize64   uint64
  UncompressedSize64 uint64
  Extra              []byte
  ExternalAttrs      uint32 // 其含义依赖于CreatorVersion
  Comment            string
}

        // Function
            func (h *FileHeader) FileInfo() os.FileInfo
            // FileInfo返回一个根据h的信息生成的 os.FileInfo
            func (h *FileHeader) ModTime() time.Time
            // 返回最近一次修改的UTC时间。(精度2s)
            func (h *FileHeader) Mode() (mode os.FileMode)
            // Mode返回h的权限和模式位
            func (h *FileHeader) SetModTime(t time.Time)
            // 将ModifiedTime和ModifiedDate字段设置为给定的UTC时间。(精度2s)
            func (h *FileHeader) SetMode(mode os.FileMode)
            // SetMode修改h的权限和模式位。

    // ReadCloser
type ReadCloser struct {
  Reader
}    
        // Function
            func (rc *ReadCloser) Close() error
            // Close关闭 zip 文件,使它不能用于I/O。
    
    // Reader
type Reader struct {
	r             io.ReaderAt
	File          []*File
	Comment       string
	decompressors map[uint16]Decompressor
}

    // Writer
    // Writer类型实现了 zip 文件的写入器。
// Writer implements a zip file writer.
type Writer struct {
	cw          *countWriter
	dir         []*header
	last        *fileWriter
	closed      bool
	compressors map[uint16]Compressor
	comment     string

	// testHookCloseSizeOffset if non-nil is called with the size
	// of offset of the central directory at Close.
	testHookCloseSizeOffset func(size, offset uint64)
}

        // Function
            func (w *Writer) Close() error
            // Close方法通过写入中央目录关闭该* Writer 。本方法不会也没办法关闭下层的 io.Writer 接口。
            func (w *Writer) Create(name string) (io.Writer, error)
            // 使用给出的文件名添加一个文件进 zip 文件。本方法返回一个 io.Writer 接口(用于写入新添加文件的内容)。文件名必须是相对路径,不能以设备或斜杠开始,只接受’/'作为路径分隔。新增文件的内容必须在下一次调用 CreateHeader 、Create或 Close 方法之前全部写入
            func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error)
            // 使用给出的*FileHeader来作为文件的元数据添加一个文件进 zip 文件。本方法返回一个 io.Writer 接口(用于写入新添加文件的内容)。新增文件的内容必须在下一次调用CreateHeader、 Create 或 Close 方法之前全部写入。

    
    // Type
        type Compressor func(io.Writer) (io.WriteCloser, error)
        // Compressor函数类型会返回一个 io.WriteCloser ,该接口会将数据压缩后写入提供的接口。关闭时,应将缓冲中的数据刷新到下层接口中。
        type Decompressor func(io.Reader) io.ReadCloser
        // Decompressor函数类型会返回一个 io.ReadCloser ,该接口的Read方法会将读取自提供的接口的数据提前解压缩。程序员有责任在读取结束时关闭该 io.ReadCloser 。

    // Function
        func FileInfoHeader(fi os.FileInfo) (*FileHeader, error)
        // FileInfoHeader返回一个根据fi填写了部分字段的Header。因为 os.FileInfo 接口的Name方法只返回它描述的文件的无路径名,有可能需要将返回值的Name字段修改为文件的完整路径名。
        func NewReader(r io.ReaderAt, size int64) (*Reader, error)
        // NewReader返回一个从r读取数据的*Reader,r被假设其大小为size字节。
        func NewWriter(w io.Writer) *Writer
        // NewWriter创建并返回一个将 zip 文件写入w的*Writer
        func OpenReader(name string) (*ReadCloser, error)
        // OpenReader会打开name指定的 zip 文件并返回一个*ReadCloser
        func RegisterCompressor(method uint16, comp Compressor)
        // RegisterCompressor使用指定的方法ID注册一个Compressor类型函数。常用的方法Store和Deflate是内建的
        func RegisterDecompressor(method uint16, d Decompressor)
        // RegisterDecompressor使用指定的方法ID注册一个Decompressor类型函数。

Demo


//压缩Demo
func zipFunc() {
	zipTarget := "./zipFile.zip"
	fileFolder := "./fileFolder"
	zipfile, err := os.Create(zipTarget)
	if err != nil {
		// if file is exist then delete file
		if err == os.ErrExist {
			if err := os.Remove(zipTarget); err != nil {
				fmt.Println(err)
			}
		} else {
			fmt.Println(err)
		}
	}
	defer zipfile.Close()
	zipwriter := zip.NewWriter(zipfile)
	defer zipwriter.Close()
	sfileInfo, err := os.Stat(fileFolder)
	if err != nil {
		fmt.Println(err)
	}
	if !sfileInfo.IsDir() {
		zipFile(fileFolder, zipwriter)
	} else {
		zipFolder(fileFolder, zipwriter)
	}
}

// 压缩文件
func zipFolder(directory string, zw *zip.Writer) error {
	return filepath.Walk(directory, func(zipPath string, file os.FileInfo, err error) error {
		//read the file failure
		if file == nil {
			return err
		}
		if file.IsDir() {
			if directory == zipPath {
				return nil
			}
			fileFolder := filepath.Join(directory, strings.TrimPrefix(zipPath, directory))
			os.Mkdir(strings.TrimPrefix(directory, file.Name()), os.ModeDir)
			//如果是目录,递归目录
			return zipFolder(fileFolder, zw)
		}
		return zipFile(zipPath, zw)
	})
}

// 解压缩函数
func zipFile(sourceFile string, zw *zip.Writer) error {
	sfile, err := os.Open(sourceFile)
	if err != nil {
		fmt.Println(err)
		return err
	}
	defer sfile.Close()

	info, err := sfile.Stat()
	if err != nil {
		fmt.Println(err)
		return err
	}
	// 获取压缩头信息
	header, err := zip.FileInfoHeader(info)
	if err != nil {
		fmt.Println(err)
		return err
	}
	// 指定文件压缩方式 默认为 Store 方式 该方式不压缩文件 只是转换为zip保存
	header.Method = zip.Deflate
	header.Name = sourceFile
	fw, err := zw.CreateHeader(header)
	if err != nil {
		fmt.Println(err)
		return err
	}
	// 写入文件到压缩包中
	_, err = io.Copy(fw, sfile)
	if err != nil {
		fmt.Println(err)
		return err
	}
	return nil
}

//untarFile 解压
func unZipFile(zipFile string, unzipPath string) error {
	//打开要解包的文件,tarFile是要解包的 .tar 文件的路径
	reader, err := zip.OpenReader(zipFile)
	if err != nil {
		fmt.Println(err)
	}
	//用 tr.Next() 来遍历包中的文件,然后将文件的数据保存到磁盘中
	for _, file := range reader.File {
		rc, err := file.Open()
		//先创建目录
		fileName := unzipPath + "/" + file.Name
		dir := path.Dir(fileName)
		_, err = os.Stat(dir)
		//如果err 为空说明文件夹已经存在,就不用创建
		if err != nil {
			err = os.MkdirAll(dir, os.ModePerm)
			if err != nil {
				fmt.Println(err)
				return err
			}
		}
		//创建空文件,准备写入解压后的数据
		fw, er := os.Create(fileName)
		if er != nil {
			fmt.Println(err)
			return er
		}
		defer fw.Close()
		// 写入解压后的数据
		_, er = io.CopyN(fw, rc, int64(file.UncompressedSize64))
		if er != nil {
			return er
		}
	}
	return nil
}
posted @ 2020-03-26 13:27  Binb  阅读(681)  评论(0编辑  收藏  举报