代码拉取完成,页面将自动刷新
package utils
import (
"archive/tar"
"bufio"
"bytes"
"errors"
"fmt"
"github.com/klauspost/pgzip"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"time"
)
var (
// .gz的header byte
gzHeaderBytesTag = []byte{0x1f, 0x8b}
tarHeaderBytesTag = []byte{0x75, 0x73, 0x74, 0x61, 0x72} // at offset 257
)
const (
nullType = ""
tarType = "tar"
gzType = "gz"
targzType = "tar.gz"
)
type unCompress struct {
error error // Error信息
compressType string // 压缩包类型
compressReader *bufio.Reader // Reader
deferFunc func()
}
func InitUnCompress(sourceReader io.Reader) (obj *unCompress) {
obj = new(unCompress)
obj.deferFunc = func() {}
// 初始化检查压缩包类型
// 新建bufioReader
reader := bufio.NewReader(sourceReader)
// 判断压缩包类型
compressType, err := getCompressType(reader, 0)
if err != nil {
obj.error = err
return
}
// 是个gz则读取一下
if compressType == gzType {
gzReader, err := pgzip.NewReader(reader)
if err != nil {
obj.error = err
return
}
obj.deferFunc = func() {
gzReader.Close()
}
reader = bufio.NewReader(gzReader)
}
// 判断是否是个tar
compressType2, err := getCompressType(reader, 257)
if err != nil {
obj.error = err
return
}
if compressType2 == tarType && compressType == gzType {
compressType = targzType
} else if compressType2 == tarType {
compressType = tarType
}
if compressType == nullType {
obj.error = errors.New("unsupported compress")
return
}
obj.compressType = compressType
obj.compressReader = reader
return
}
func (p *unCompress) GetCompressType() (compressType string, err error) {
if p.error != nil {
err = p.error
return
}
defer p.deferFunc()
return p.compressType, nil
}
// UnCompress 正式的解压操作,返回解压的路径和错误信息
// 用完记得删除压缩文件和解压目录
func (p *unCompress) UnCompress(destDir string) (destPath string, err error) {
if p.error != nil {
err = p.error
return
}
defer p.deferFunc()
// 确认解压目录
destDir, err = makeDestDir(destDir)
if err != nil {
p.error = err
return
}
// 判断类型,执行解压
if p.compressType == tarType || p.compressType == targzType {
destPath, err = unTar(p.compressReader, destDir)
p.error = err
return
}
// 如果不是tar或者tar.gz 那么将它保存到磁盘上
fPath := filepath.Join(destDir, sanitizeArchiveName(path.Base("unknown-compress")))
// 创建文件
file, err := os.Create(fPath)
if err != nil {
p.error = err
return
}
defer file.Close()
// copy
if _, err = io.Copy(file, p.compressReader); err != nil {
p.error = err
return
}
return destDir, nil
}
// makeDestDir 创建解压目录
func makeDestDir(destDir string) (newDestDir string, err error) {
newDestDir = destDir
// 判断解压路径是否指定了
if len(destDir) == 0 {
newDestDir, err = ioutil.TempDir(os.TempDir(), "uncompress-")
}
// 确认destDir 目录是否存在,不存在则创建
err = os.MkdirAll(newDestDir, 0740)
return
}
func unTar(compressReader io.Reader, destDir string) (string, error) {
tarReader := tar.NewReader(compressReader)
// 遍历迭代压缩包
rootDir := destDir
for {
itemArchive, err := tarReader.Next()
// 遍历结束
if err == io.EOF {
break
}
if err != nil {
return rootDir, err
}
// 跳过 pax_global_header
if itemArchive.Name == "pax_global_header" {
continue
}
// 组装包文件路径
fPath := filepath.Join(destDir, sanitizeArchiveName(itemArchive.Name))
// 如果包文件是个目录则创建
if itemArchive.FileInfo().IsDir() {
if rootDir == destDir {
rootDir = fPath
}
if err = os.MkdirAll(fPath, os.FileMode(itemArchive.Mode)); err != nil {
return rootDir, err
}
continue
}
// 处理包文件
_, err = unTarFile(itemArchive, tarReader, fPath, rootDir)
if err != nil {
return rootDir, err
}
}
return rootDir, nil
}
func unTarFile(itemArchive *tar.Header, tarReader *tar.Reader, fPath, rootDir string) (string, error) {
// 创建包文件
file, err := os.Create(fPath)
if err != nil {
return rootDir, err
}
defer file.Close()
// 处理包文件的权限
if err = file.Chmod(os.FileMode(itemArchive.Mode)); err != nil {
return rootDir, fmt.Errorf("set archive file permissions err %q: %#v", file.Name(), err)
}
// 处理包文件时间
if err = os.Chtimes(file.Name(), time.Now(), itemArchive.ModTime); err != nil {
return rootDir, fmt.Errorf("set archive file atime and mtime err %q: %#v", file.Name(), err)
}
// 拷贝
_, err = io.Copy(file, tarReader)
if err != nil {
return rootDir, fmt.Errorf("copy archive file err %q: %#v", file.Name(), err)
}
return rootDir, nil
}
// Sanitizes 处理包中文件名称,以避免在取消归档时覆盖敏感系统文件
func sanitizeArchiveName(name string) string {
// 忽略Windows中的卷驱动器标签
if len(name) > 1 && name[1] == ':' && runtime.GOOS == "windows" {
name = name[2:]
}
name = filepath.Clean(name)
name = filepath.ToSlash(name)
for strings.HasPrefix(name, "../") {
name = name[3:]
}
return name
}
// getCompressType 获取压缩包类型
func getCompressType(reader *bufio.Reader, offset int) (string, error) {
hBytes, err := reader.Peek(offset + 6)
if err != nil {
if err == io.EOF {
err = nil
}
return nullType, err
}
magic := hBytes[offset : offset+6]
if bytes.Equal(tarHeaderBytesTag, magic[0:5]) {
return tarType, nil
}
if bytes.Equal(gzHeaderBytesTag, magic[0:2]) {
return gzType, nil
}
return nullType, nil
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。