package models

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

/*
	z := MyZip{}

要压缩成一个zip的多个文件的路径
files := []string{"abc/SelectRvvwd.jpg", "abc/SelectRvvwd (1).jpg", "SelectRvvwd (2).jpg"}
//设置输出的zip的路径
output := "./xyz.zip"
err := z.ZipFiles(output, files)

	if err != nil {
		fmt.Println(err)
	}

output := "C:\\Users\\Administrator\\Desktop\\gotest"
err1:=z.UnZip(output, "xyz.zip")

	if err != nil {
		fmt.Println(err)
	}
*/
type MyZip struct {
}

// 1.压缩包保存路径+名字   2.文件名 数组
func (class *MyZip) ZipFiles(filename string, files []string) error {
	//创建输出文件目录
	newZipFile, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer newZipFile.Close()
	//创建空的zip档案，可以理解为打开zip文件，准备写入
	zipWriter := zip.NewWriter(newZipFile)
	defer zipWriter.Close()
	// Add files to zip
	for _, file := range files {
		if err = class.AddFileToZip(zipWriter, file); err != nil {
			return err
		}
	}
	return nil
}

func (class *MyZip) AddFileToZip(zipWriter *zip.Writer, filename string) error {
	//打开要压缩的文件
	fileToZip, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer fileToZip.Close()
	//获取文件的描述
	info, err := fileToZip.Stat()
	if err != nil {
		return err
	}
	//FileInfoHeader返回一个根据fi填写了部分字段的Header，可以理解成是将fileinfo转换成zip格式的文件信息
	header, err := zip.FileInfoHeader(info)
	if err != nil {
		return err
	}
	header.Name = filename
	/*
	   预定义压缩算法。
	   archive/zip包中预定义的有两种压缩方式。一个是仅把文件写入到zip中。不做压缩。一种是压缩文件然后写入到zip中。默认的Store模式。就是只保存不压缩的模式。
	   Store   unit16 = 0  //仅存储文件
	   Deflate unit16 = 8  //压缩文件
	*/
	header.Method = zip.Deflate
	//创建压缩包头部信息
	writer, err := zipWriter.CreateHeader(header)
	if err != nil {
		return err
	}
	//将源复制到目标，将fileToZip 写入writer   是按默认的缓冲区32k循环操作的，不会将内容一次性全写入内存中,这样就能解决大文件的问题
	_, err = io.Copy(writer, fileToZip)
	return err
}

// 解压缩 1.输出路径 2.解压缩的文件名
func (class *MyZip) UnZip(output string, zipName string) error {
	archive, err := zip.OpenReader(zipName)
	if err != nil {
		return errors.New("打开压缩文件失败")
	}
	defer archive.Close()
	for _, f := range archive.File {
		filePath := filepath.Join(output, f.Name)
		if !strings.HasPrefix(filePath, filepath.Clean(output)+string(os.PathSeparator)) {
			return errors.New("路径错误")
		}
		if f.FileInfo().IsDir() {
			os.MkdirAll(filePath, os.ModePerm)
			continue
		}
		if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
			return errors.New("创建文件夹失败")
		}
		dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
		if err != nil {
			return errors.New("创建文件失败")
		}
		fileInArchive, err := f.Open()
		if err != nil {
			return errors.New("读取压缩包文件失败")
		}
		if _, err := io.Copy(dstFile, fileInArchive); err != nil {
			return errors.New("解压失败")
		}
		dstFile.Close()
		fileInArchive.Close()
		return nil
	}
	return errors.New("压缩包文件为空")
}
