Go 解压/压缩

🏠 回到主页

Golang 解压/压缩 zip 文件

解压zip文件到指定目录,包含顶层文件夹

package zip

import (
	"archive/zip"
	"io"
	"os"
	"path/filepath"
	"strings"
)

type ZipHandler struct {
}

func NewZipHandler() *ZipHandler {
	return &ZipHandler{}
}

func (z *ZipHandler) UnZip(src, dst string) error {
	archive, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer func(archive *zip.ReadCloser) {
		err := archive.Close()
		if err != nil {
			return
		}
	}(archive)

	for _, item := range archive.File {
		filePath := filepath.Join(dst, item.Name)

		if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
			return err
		}

		if item.FileInfo().IsDir() {
			err := os.MkdirAll(filePath, os.ModePerm)
			if err != nil {
				return err
			}
			continue
		}

		file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, item.Mode())
		if err != nil {
			return err
		}

		fileInArchive, err := item.Open()
		if err != nil {
			return err
		}

		if _, err := io.Copy(file, fileInArchive); err != nil {
			return err
		}
	}
	return nil
}

 

解压zip文件到指定目录,不包含顶层文件夹,只是将文件解压到指定目录

这种适合压缩文件带了顶层文件夹,但是解压后不想要顶层文件夹的情况

package zip

import (
	"archive/zip"
	"io"
	"os"
	"path/filepath"
	"strings"
)

type ZipHandler struct {
}

func NewZipHandler() *ZipHandler {
	return &ZipHandler{}
}
            
func (z *ZipHandler) UnZipTo(src, dst string) error {
	archive, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer func(archive *zip.ReadCloser) {
		err := archive.Close()
		if err != nil {
			return
		}
	}(archive)

	for _, item := range archive.File {
		splitPath := strings.Split(item.Name, string(os.PathSeparator))
		targetPath := strings.Join(splitPath[1:], string(os.PathSeparator))
		filePath := filepath.Join(dst, targetPath)

		if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
			return err
		}

		if item.FileInfo().IsDir() {
			err := os.MkdirAll(filePath, os.ModePerm)
			if err != nil {
				return err
			}
			continue
		}

		file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, item.Mode())
		if err != nil {
			return err
		}
		fileInArchive, err := item.Open()
		if err != nil {
			return err
		}
		if _, err := io.Copy(file, fileInArchive); err != nil {
			return err
		}
	}
	return nil
}

 

压缩zip文件

Golang 中可以使用 archive/zip 包来进行文件的压缩和解压缩操作。以下是一个简单的示例程序,演示如何将一个文件夹压缩为 zip 文件:

package main

import (
	"archive/zip"
	"io"
	"os"
	"path/filepath"
)

func main() {
	// 创建一个新的 zip 文件
	zipfile, err := os.Create("test.zip")
	if err != nil {
		panic(err)
	}
	defer zipfile.Close()

	// 创建一个 zip.Writer
	zipWriter := zip.NewWriter(zipfile)

	// 遍历要压缩的文件夹
	dir := "./test"
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		// 如果是文件夹或者无法读取文件信息,则忽略
		if info.IsDir() || err != nil {
			return nil
		}

		// 打开文件
		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()

		// 创建一个新的文件
		zipFile, err := zipWriter.Create(path)
		if err != nil {
			return err
		}

		// 将文件内容写入到 zip 文件中
		_, err = io.Copy(zipFile, file)
		if err != nil {
			return err
		}

		return nil
	})

	// 关闭 zip.Writer
	err = zipWriter.Close()
	if err != nil {
		panic(err)
	}
}

 

该示例程序将指定的文件夹(“./test”)中的所有文件压缩到一个名为 “test.zip” 的 zip 文件中。程序首先创建一个新的 zip 文件,并创建一个 zip.Writer。然后遍历要压缩的文件夹,对于每个文件,打开文件,创建一个新的 zip 文件条目(zip.Writer.Create),并将文件内容写入到 zip 文件中(io.Copy),最后关闭 zip.Writer

 

 

Golang 解压/压缩 tar.gz 文件

解压tar.gz文件到指定目录,包含顶层文件夹

package tar

import (
	"archive/tar"
	"compress/gzip"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
)

type TGZHandler struct {
}

func NewTGZHandler() *TGZHandler {
	return &TGZHandler{}
}

func (z *TGZHandler) UNTarGZ(src, dst string) error {
	archive, err := os.Open(src)
	if err != nil {
		return err
	}
	defer func(archive *os.File) {
		err := archive.Close()
		if err != nil {
			return
		}
	}(archive)

	gr, err := gzip.NewReader(archive)
	if err != nil {
		return err
	}

	defer func(gr *gzip.Reader) {
		err := gr.Close()
		if err != nil {
			return
		}
	}(gr)

	tr := tar.NewReader(gr)
	for {
		f, err := tr.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		filePath := path.Join(dst, f.Name)
		if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
			return err
		}

		if f.FileInfo().IsDir() {
			err := os.MkdirAll(filePath, os.ModePerm)
			if err != nil {
				return err
			}
			continue
		} else {
			fileName := path.Join(dst, f.Name)
			fw, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(f.Mode))
			defer func(fw *os.File) {
				err := fw.Close()
				if err != nil {
					return
				}
			}(fw)
			if err != nil {
				return err
			}
			if _, err := io.Copy(fw, tr); err != nil {
				return err
			}
		}

	}

	return nil
}

 

解压tar.gz文件到指定目录,不包含顶层文件夹,只是将文件解压到指定目录

这种适合压缩文件带了顶层文件夹,但是解压后不想要顶层文件夹的情况

package tar

import (
	"archive/tar"
	"compress/gzip"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
)

type TGZHandler struct {
}

func NewTGZHandler() *TGZHandler {
	return &TGZHandler{}
}

func (z *TGZHandler) UNTarGZTo(src, dst string) error {
	archive, err := os.Open(src)
	if err != nil {
		return err
	}
	defer func(archive *os.File) {
		err := archive.Close()
		if err != nil {
			return
		}
	}(archive)

	gr, err := gzip.NewReader(archive)
	if err != nil {
		return err
	}

	defer func(gr *gzip.Reader) {
		err := gr.Close()
		if err != nil {
			return
		}
	}(gr)

	tr := tar.NewReader(gr)
	for {
		f, err := tr.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		filePath := path.Join(dst, f.Name)
		if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
			return err
		}

		if f.FileInfo().IsDir() {
			err := os.MkdirAll(filePath, os.ModePerm)
			if err != nil {
				return err
			}
			continue
		} else {
			splitPath := strings.Split(f.Name, string(os.PathSeparator))
			targetPath := strings.Join(splitPath[1:], string(os.PathSeparator))
			filePath := filepath.Join(dst, targetPath)
			fw, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(f.Mode))
			defer func(fw *os.File) {
				err := fw.Close()
				if err != nil {
					return
				}
			}(fw)
			if err != nil {
				return err
			}
			if _, err := io.Copy(fw, tr); err != nil {
				return err
			}
		}

	}

	return nil
}

 

压缩tar.gz文件

Golang 中可以使用 archive/tar 和 compress/gzip 包来将文件夹压缩为 .tar.gz 文件。以下是一个示例程序,演示如何将文件夹压缩为 .tar.gz 文件:

package main

import (
	"archive/tar"
	"compress/gzip"
	"io"
	"os"
	"path/filepath"
)

func main() {
	// 创建一个新的 .tar.gz 文件
	file, err := os.Create("test.tar.gz")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	// 创建一个 gzip.Writer
	gzipWriter := gzip.NewWriter(file)
	defer gzipWriter.Close()

	// 创建一个 tar.Writer
	tarWriter := tar.NewWriter(gzipWriter)
	defer tarWriter.Close()

	// 遍历要压缩的文件夹
	dir := "./test"
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		// 如果是文件夹或者无法读取文件信息,则忽略
		if info.IsDir() || err != nil {
			return nil
		}

		// 打开文件
		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()

		// 创建一个新的 tar 文件头
		header := new(tar.Header)
		header.Name = path
		header.Size = info.Size()
		header.Mode = int64(info.Mode())
		header.ModTime = info.ModTime()

		// 将 tar 文件头写入到 tar 文件中
		err = tarWriter.WriteHeader(header)
		if err != nil {
			return err
		}

		// 将文件内容写入到 tar 文件中
		_, err = io.Copy(tarWriter, file)
		if err != nil {
			return err
		}

		return nil
	})
}

 

该示例程序将指定的文件夹(“./test”)中的所有文件压缩到一个名为 “test.tar.gz” 的 .tar.gz 文件中。程序首先创建一个新的 .tar.gz 文件,并创建一个 gzip.Writer 和一个 tar.Writer。然后遍历要压缩的文件夹,对于每个文件,打开文件,创建一个新的 tar 文件头(tar.Header),并将文件内容写入到 tar 文件中,最后关闭 tar.Writer 和 gzip.Writer

 

如何判断文件是zip压缩的还是tgz压缩的

要判断文件是 ZIP 压缩还是 TGZ 压缩,可以使用文件格式识别库 libmagic,或者根据文件后缀名来判断。以下是两种方法的示例代码:

使用 libmagic 库判断文件类型:

package main

import (
	"fmt"
	"github.com/rakyll/magicmime"
	"os"
)

func main() {
	fileName := "test.zip"

	// 创建 magicmime 实例
	magic := magicmime.NewMagicMime("magic.mime")

	// 打开文件
	file, err := os.Open(fileName)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()

	// 判断文件类型
	fileType, err := magic.TypeByFile(fileName)
	if err != nil {
		fmt.Println(err)
		return
	}

	if fileType == "Zip archive data, at least v2.0 to extract" {
		fmt.Println("文件类型:ZIP")
	} else if fileType == "gzip compressed data, from Unix" {
		fmt.Println("文件类型:TGZ")
	} else {
		fmt.Printf("未知文件类型:%s\n", fileType)
	}
}

 

该示例程序使用 go-magic 库来判断文件类型,并根据文件类型输出相应的信息。如果文件类型是 Zip 压缩,输出“文件类型:ZIP”,如果是 TGZ 压缩,输出“文件类型:TGZ”,否则输出“未知文件类型:文件类型”

根据文件后缀名判断文件类型:

package main

import (
	"fmt"
	"strings"
)

func main() {
	fileName := "test.zip"

	// 获取文件后缀名
	fileExt := strings.ToLower(fileName[strings.LastIndex(fileName, ".")+1:])

	if fileExt == "zip" {
		fmt.Println("文件类型:ZIP")
	} else if fileExt == "tgz" || fileExt == "tar.gz" {
		fmt.Println("文件类型:TGZ")
	} else {
		fmt.Printf("未知文件类型:%s\n", fileExt)
	}
}

 

该示例程序根据文件后缀名来判断文件类型,并输出相应的信息。如果文件后缀名是 zip,输出“文件类型:ZIP”,如果是 tgz 或 tar.gz,输出“文件类型:TGZ”,否则输出“未知文件类型:文件后缀名”

 

如何判断给定的文件是哪种文件类型

可以使用 http.DetectContentType() 函数来判断给定的文件类型。该函数会根据文件内容的前几个字节来判断文件类型。

以下是一个示例代码,可以判断文件类型并输出文件类型:

package main

import (
	"fmt"
	"net/http"
	"os"
)

func main() {
	fileName := "test.jpg"

	// 打开文件
	file, err := os.Open(fileName)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()

	// 读取文件内容的前512个字节
	buffer := make([]byte, 512)
	_, err = file.Read(buffer)
	if err != nil {
		fmt.Println(err)
		return
	}

	// 判断文件类型
	fileType := http.DetectContentType(buffer)

	fmt.Printf("文件类型: %s\n", fileType)
}

 

该示例程序首先打开指定的文件,并读取文件内容的前512个字节。然后,它使用 http.DetectContentType() 函数来判断文件类型,并输出文件类型

请注意,http.DetectContentType() 函数并不是百分之百准确的,因为它只是根据文件内容的前几个字节来判断文件类型。在一些特殊情况下,该函数可能会判断错误

 

将一个100g的文件以5g为单位压缩为20个子zip包

在Go语言中,可以使用标准库中的archive/zipio包来实现将一个大文件分割为多个子Zip包的功能。以下是一个示例代码:

package main

import (
    "archive/zip"
    "fmt"
    "io"
    "os"
)

func main() {
    filePath := "largefile.bin" // 待压缩的大文件路径
    chunkSize := int64(5 * 1024 * 1024 * 1024) // 每个子Zip包的大小,这里设置为5GB
    targetDir := "target" // 保存子Zip包的目标文件夹路径

    // 打开大文件
    file, err := os.Open(filePath)
    if err != nil {
        panic(err)
    }
    defer file.Close()

    // 获取大文件的大小
    fileInfo, err := file.Stat()
    if err != nil {
        panic(err)
    }
    fileSize := fileInfo.Size()

    // 计算需要分割成多少个子Zip包
    numChunks := fileSize / chunkSize
    if fileSize % chunkSize != 0 {
        numChunks++
    }

    // 创建目标文件夹
    os.MkdirAll(targetDir, os.ModePerm)

    // 分割大文件并保存为多个子Zip包
    for i := int64(0); i < numChunks; i++ {
        start := i * chunkSize
        end := (i + 1) * chunkSize
        if end > fileSize {
            end = fileSize
        }

        // 创建子Zip包的文件名
        zipFileName := fmt.Sprintf("%s/part-%02d.zip", targetDir, i)

        // 创建子Zip包
        zipFile, err := os.Create(zipFileName)
        if err != nil {
            panic(err)
        }
        defer zipFile.Close()

        // 创建Zip Writer
        zipWriter := zip.NewWriter(zipFile)

        // 将大文件的一部分写入Zip包中
        fileChunk := make([]byte, end-start)
        _, err = file.ReadAt(fileChunk, start)
        if err != nil && err != io.EOF {
            panic(err)
        }

        // 创建Zip包中的文件
        zipEntryName := fmt.Sprintf("part-%02d.bin", i)
        zipEntry, err := zipWriter.Create(zipEntryName)
        if err != nil {
            panic(err)
        }

        // 写入Zip包中的文件内容
        _, err = zipEntry.Write(fileChunk)
        if err != nil {
            panic(err)
        }

        // 关闭Zip Writer
        err = zipWriter.Close()
        if err != nil {
            panic(err)
        }

        fmt.Printf("已创建子Zip包:%s\n", zipFileName)
    }

    fmt.Println("分割完成")
}

在上面的代码中,我们首先使用os.Open函数打开待压缩的大文件,然后使用file.Stat()函数获取文件的大小。接着,我们根据每个子Zip包的大小,计算需要将大文件分割成多少个子Zip包。然后,我们创建一个目标文件夹来保存子Zip包。

接下来,我们使用一个循环来分割大文件并将每个子文件保存为一个Zip包。对于每个子文件,我们首先创建一个子Zip包的文件名,然后创建一个Zip Writer来写入Zip包中的文件。我们使用file.ReadAt函数读取大文件中的一部分数据,并将其写入Zip包中的文件。最后,我们关闭Zip Writer,并输出一条信息,表示已经创建子Zip包。

最终,当所有子Zip包都创建完成后,我们输出一条信息,表示分割完成

 

将上面20个zip子包合并为一个原始zip文件

package main

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"path/filepath"
)

const (
	zipFileName = "merged.zip"
)

func main() {
	// 创建一个新的zip文件
	newZipFile, err := os.Create(zipFileName)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer newZipFile.Close()

	// 创建一个zip写入器
	zipWriter := zip.NewWriter(newZipFile)
	defer zipWriter.Close()

	// 遍历所有zip子包文件
	for i := 0; i < 20; i++ {
		zipFileName := fmt.Sprintf("file_%d.zip", i+1)

		// 打开zip子包文件
		zipFile, err := os.Open(zipFileName)
		if err != nil {
			fmt.Println(err)
			return
		}
		defer zipFile.Close()

		// 获取zip子包文件的文件信息
		info, err := zipFile.Stat()
		if err != nil {
			fmt.Println(err)
			return
		}

		// 创建一个zip文件头
		header, err := zip.FileInfoHeader(info)
		if err != nil {
			fmt.Println(err)
			return
		}

		// 设置zip文件头的名称
		header.Name = filepath.Base(zipFileName)

		// 向zip文件中写入zip文件头
		writer, err := zipWriter.CreateHeader(header)
		if err != nil {
			fmt.Println(err)
			return
		}

		// 将zip子包文件的内容写入到新的zip文件中
		_, err = io.Copy(writer, zipFile)
		if err != nil {
			fmt.Println(err)
			return
		}
	}

	fmt.Printf("合并完成,新的zip文件名为:%s\n", zipFileName)
}

 

在这个示例程序中,我们首先创建了一个新的zip文件merged.zip,然后通过创建zip.Writer来写入zip文件。接着,我们遍历所有的zip子包文件,打开每个zip子包文件,读取文件信息并创建一个新的zip文件头,然后将zip子包文件的内容写入到新的zip文件中。最后,我们输出合并后的zip文件名并结束程序

请注意,这个示例程序仅适用于zip子包文件中的所有文件都位于根目录下的情况。如果zip子包文件中包含子目录,你可能需要修改代码以正确处理这些文件

 

 

🏠 回到主页

 

posted @ 2023-02-15 20:32  SuperCodeX  阅读(818)  评论(0编辑  收藏  举报