
// 








namespace SafeCampus.Core.Utils;

public class ZipUtils
{
    /// <summary>
    /// 压缩单个文件
    /// </summary>
    /// <param name="fileToZip">要压缩的文件</param>
    /// <param name="zipedFile">压缩后的文件</param>
    /// <param name="compressionLevel">压缩等级</param>
    /// <param name="blockSize">每次写入大小</param>
    public static void ZipFile(string fileToZip, string zipedFile, int compressionLevel,
        int blockSize)
    {
        //如果文件没有找到，则报错
        if (!File.Exists(fileToZip))
        {
            throw new FileNotFoundException("指定要压缩的文件: " + fileToZip + " 不存在!");
        }

        using (var zipFile = File.Create(zipedFile))
        {
            using (var zipStream = new ZipOutputStream(zipFile))
            {
                using (var streamToZip = new FileStream(fileToZip, FileMode.Open, FileAccess.Read))
                {
                    var fileName = fileToZip.Substring(fileToZip.LastIndexOf("\\", StringComparison.Ordinal) + 1);

                    var zipEntry = new ZipEntry(fileName);

                    zipStream.PutNextEntry(zipEntry);

                    zipStream.SetLevel(compressionLevel);

                    var buffer = new byte[blockSize];

                    try
                    {
                        var sizeRead = 0;
                        do
                        {
                            sizeRead = streamToZip.Read(buffer, 0, buffer.Length);
                            zipStream.Write(buffer, 0, sizeRead);
                        } while (sizeRead > 0);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }

                    streamToZip.Close();
                }

                zipStream.Finish();
                zipStream.Close();
            }

            zipFile.Close();
        }
    }

    /// <summary>
    /// 压缩单个文件
    /// </summary>
    /// <param name="fileToZip">要进行压缩的文件名</param>
    /// <param name="zipedFile">压缩后生成的压缩文件名</param>
    public static void ZipFile(string fileToZip, string zipedFile)
    {
        //如果文件没有找到，则报错
        if (!File.Exists(fileToZip))
        {
            throw new FileNotFoundException("指定要压缩的文件: " + fileToZip + " 不存在!");
        }

        using (var fs = File.OpenRead(fileToZip))
        {
            var buffer = new byte[fs.Length];
            fs.Read(buffer, 0, buffer.Length);
            fs.Close();

            using (var zipFile = File.Create(zipedFile))
            {
                using (var zipStream = new ZipOutputStream(zipFile))
                {
                    var fileName = fileToZip.Substring(fileToZip.LastIndexOf("\\") + 1);
                    var zipEntry = new ZipEntry(fileName);
                    zipStream.PutNextEntry(zipEntry);
                    zipStream.SetLevel(5);

                    zipStream.Write(buffer, 0, buffer.Length);
                    zipStream.Finish();
                    zipStream.Close();
                }
            }
        }
    }

    /// <summary>
    /// 压缩多个文件到指定路径
    /// </summary>
    /// <param name="sourceFileNames">压缩到哪个路径</param>
    /// <param name="zipFileName">压缩文件名称</param>
    public static void ZipFile(List<string> sourceFileNames, string zipFileName)
    {
        //压缩文件打包
        using (var s = new ZipOutputStream(File.Create(zipFileName)))
        {
            s.SetLevel(9);
            var buffer = new byte[4096];
            foreach (var file in sourceFileNames)
            {
                if (Directory.Exists(file))// 先当作目录处理如果存在这个目录就递归Copy该目录下面的文件
                {
                    var pPath = "";
                    pPath += Path.GetFileName(file);
                    pPath += "\\";
                    ZipSetp(file, s, pPath, sourceFileNames);
                }
                else// 否则直接压缩文件
                {
                    var entry = new ZipEntry(Path.GetFileName(file));
                    entry.DateTime = DateTime.Now;
                    s.PutNextEntry(entry);
                    using (var fs = File.OpenRead(file))
                    {
                        int sourceBytes;
                        do
                        {
                            sourceBytes = fs.Read(buffer, 0, buffer.Length);
                            s.Write(buffer, 0, sourceBytes);
                        } while (sourceBytes > 0);
                    }
                }
            }
            s.Finish();
            s.Close();
        }
    }

    /// <summary>
    /// 压缩多层目录
    /// </summary>
    /// <param name="strDirectory">待压缩目录</param>
    /// <param name="zipedFile">压缩后生成的压缩文件名，绝对路径</param>
    public static void ZipFileDirectory(string strDirectory, string zipedFile)
    {
        using (var zipFile = File.Create(zipedFile))
        {
            using (var s = new ZipOutputStream(zipFile))
            {
                s.SetLevel(9);
                ZipSetp(strDirectory, s, "");
            }
        }
    }

    /// <summary>
    /// 压缩多层目录
    /// </summary>
    /// <param name="strDirectory">待压缩目录</param>
    /// <param name="zipedFile">压缩后生成的压缩文件名，绝对路径</param>
    /// <param name="files">指定要压缩的文件列表(完全路径)</param>
    public static void ZipFileDirectory(string strDirectory, string zipedFile, List<string> files)
    {
        using (var zipFile = File.Create(zipedFile))
        {
            using (var s = new ZipOutputStream(zipFile))
            {
                s.SetLevel(9);
                ZipSetp(strDirectory, s, "", files);
            }
        }
    }

    /// <summary>
    /// 递归遍历目录
    /// </summary>
    /// <param name="strDirectory">需遍历的目录</param>
    /// <param name="s">压缩输出流对象</param>
    /// <param name="parentPath">The parent path.</param>
    /// <param name="files">需要压缩的文件</param>
    private static void ZipSetp(string strDirectory, ZipOutputStream s, string parentPath,
        List<string> files = null)
    {
        if (strDirectory[strDirectory.Length - 1] != Path.DirectorySeparatorChar)
        {
            strDirectory += Path.DirectorySeparatorChar;
        }

        var filenames = Directory.GetFileSystemEntries(strDirectory);

        var buffer = new byte[4096];
        foreach (var file in filenames)// 遍历所有的文件和目录
        {
            if (files != null && !files.Contains(file))
            {
                continue;
            }
            if (Directory.Exists(file))// 先当作目录处理如果存在这个目录就递归Copy该目录下面的文件
            {
                var pPath = parentPath;
                pPath += Path.GetFileName(file);
                pPath += "\\";
                ZipSetp(file, s, pPath, files);
            }
            else// 否则直接压缩文件
            {
                //打开压缩文件
                var fileName = parentPath + Path.GetFileName(file);
                var entry = new ZipEntry(fileName);

                entry.DateTime = DateTime.Now;

                s.PutNextEntry(entry);
                using (var fs = File.OpenRead(file))
                {
                    int sourceBytes;
                    do
                    {
                        sourceBytes = fs.Read(buffer, 0, buffer.Length);
                        s.Write(buffer, 0, sourceBytes);
                    } while (sourceBytes > 0);
                }
            }
        }
    }

    /// <summary>
    /// 解压缩一个 zip 文件。
    /// </summary>
    /// <param name="zipedFile">压缩文件</param>
    /// <param name="strDirectory">解压目录</param>
    /// <param name="password">zip 文件的密码。</param>
    /// <param name="overWrite">是否覆盖已存在的文件。</param>
    public static void UnZip(string zipedFile, string strDirectory, bool overWrite,
        string password)
    {
        if (strDirectory == "")
            strDirectory = Directory.GetCurrentDirectory();
        if (!strDirectory.EndsWith("\\"))
            strDirectory = strDirectory + "\\";

        using (var s = new ZipInputStream(File.OpenRead(zipedFile)))
        {
            if (password != null)
            {
                s.Password = password;
            }
            ZipEntry theEntry;

            while ((theEntry = s.GetNextEntry()) != null)
            {
                var directoryName = "";
                var pathToZip = "";
                pathToZip = theEntry.Name;

                if (pathToZip != "")
                    directoryName = Path.GetDirectoryName(pathToZip) + "\\";

                var fileName = Path.GetFileName(pathToZip);

                Directory.CreateDirectory(strDirectory + directoryName);

                if (fileName != "")
                {
                    if (File.Exists(strDirectory + directoryName + fileName) && overWrite || !File.Exists(strDirectory + directoryName + fileName))
                    {
                        using (var streamWriter = File.Create(strDirectory + directoryName + fileName))
                        {
                            var size = 2048;
                            var data = new byte[2048];
                            while (true)
                            {
                                size = s.Read(data, 0, data.Length);

                                if (size > 0)
                                    streamWriter.Write(data, 0, size);
                                else
                                    break;
                            }
                            streamWriter.Close();
                        }
                    }
                }
            }

            s.Close();
        }
    }

    /// <summary>
    /// 解压缩一个 zip 文件。
    /// </summary>
    /// <param name="zipedFile">压缩文件</param>
    /// <param name="strDirectory">解压目录</param>
    /// <param name="overWrite">是否覆盖已存在的文件。</param>
    public static void UnZip(string zipedFile, string strDirectory, bool overWrite)
    {
        UnZip(zipedFile, strDirectory, overWrite, null);
    }

    /// <summary>
    /// 解压缩一个 zip 文件。
    /// 覆盖已存在的文件。
    /// </summary>
    /// <param name="zipedFile">压缩文件</param>
    /// <param name="strDirectory">解压目录</param>
    public static void UnZip(string zipedFile, string strDirectory)
    {
        UnZip(zipedFile, strDirectory, true);
    }

    /// <summary>
    /// 获取压缩文件中指定类型的文件
    /// </summary>
    /// <param name="zipedFile">压缩文件</param>
    /// <param name="fileExtension">文件类型(.txt|.exe)</param>
    /// <returns>文件名称列表(包含子目录)</returns>
    public static List<string> GetFiles(string zipedFile, List<string> fileExtension)
    {
        var files = new List<string>();
        if (!File.Exists(zipedFile))
        {
            //return files;
            throw new FileNotFoundException(zipedFile);
        }

        using (var s = new ZipInputStream(File.OpenRead(zipedFile)))
        {
            ZipEntry theEntry;
            while ((theEntry = s.GetNextEntry()) != null)
            {
                if (theEntry.IsFile)
                {
                    //Console.WriteLine("Name : {0}", theEntry.Name);
                    if (fileExtension != null)
                    {
                        if (fileExtension.Contains(Path.GetExtension(theEntry.Name)))
                        {
                            files.Add(theEntry.Name);
                        }
                    }
                    else
                    {
                        files.Add(theEntry.Name);
                    }
                }
            }
            s.Close();
        }

        return files;
    }

    /// <summary>
    /// 获取压缩文件中的所有文件
    /// </summary>
    /// <param name="zipedFile">压缩文件</param>
    /// <returns>文件名称列表(包含子目录)</returns>
    public static List<string> GetFiles(string zipedFile)
    {
        return GetFiles(zipedFile, null);
    }

    /// <summary>
    /// 打包线上线下文件
    /// </summary>
    /// <param name="zipName">压缩文件名称</param>
    /// <param name="fileList">文件列表</param>
    /// <param name="error">保存路径</param>
    /// <param name="isLocal">是否本地</param>
    public static string ZipFiles(string zipName, List<FileItem> fileList, out string error,
        bool isLocal = true)
    {
        error = string.Empty;

        var path = string.Format("/ZipFiles/{0}/{1}/{2}/", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
        //文件保存目录
        var directory = App.WebHostEnvironment.WebRootPath + path;

        var url = App.Configuration["FileHostUrl"].TrimEnd('/') + path + zipName;
        var savePath = directory + zipName;
        try
        {
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }

            using (var zipStream = new ZipOutputStream(File.Create(savePath)))
            {
                zipStream.SetLevel(9);//压缩级别0-9

                foreach (var item in fileList)
                {
                    byte[] buffer = null;
                    if (isLocal)
                    {
                        var stream = new FileInfo(item.FilePath).OpenRead();
                        buffer = new byte[stream.Length];
                        stream.Read(buffer, 0, Convert.ToInt32(stream.Length));
                    }
                    else
                    {
                        buffer = new WebClient().DownloadData(item.FilePath);//取消
                    }
                    var entry = new ZipEntry(item.FileName);
                    entry.DateTime = DateTime.Now;
                    entry.Size = buffer.Length;
                    zipStream.PutNextEntry(entry);
                    zipStream.Write(buffer, 0, buffer.Length);
                }
            }
        }
        catch (Exception ex)
        {
            error = "文件打包失败：" + ex.Message;
        }
        return url;
    }

    /// 压缩文件夹
    /// 要打包的文件夹
    /// 是否删除原文件夹
    public static string CompressDirectory(string dirPath, bool deleteDir)
    {
        //压缩文件路径
        var pCompressPath = dirPath + ".zip";
        if (File.Exists(pCompressPath))
            File.Delete(pCompressPath);
        //创建压缩文件
        var pCompressFile = new FileStream(pCompressPath, FileMode.Create);
        using (var zipoutputstream = new ZipOutputStream(pCompressFile))
        {
            var crc = new Crc32();
            var fileList = GetAllFies(dirPath);
            foreach (var item in fileList)
            {
                var fs = new FileStream(item.Key, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                // FileStream fs = File.OpenRead(item.Key.ToString());
                var buffer = new byte[fs.Length];
                fs.Read(buffer, 0, buffer.Length);
                var entry = new ZipEntry(item.Key.Substring(dirPath.Length));
                entry.DateTime = item.Value;
                entry.Size = fs.Length;
                fs.Close();
                crc.Reset();
                crc.Update(buffer);
                entry.Crc = crc.Value;
                zipoutputstream.PutNextEntry(entry);
                zipoutputstream.Write(buffer, 0, buffer.Length);
            }
        }
        if (deleteDir)
        {
            Directory.Delete(dirPath, true);
        }
        return pCompressPath;
    }

    ///
    /// 获取所有文件
    ///
    ///
    private static Dictionary<string, DateTime> GetAllFies(string dir)
    {
        var filesList = new Dictionary<string, DateTime>();
        var fileDire = new DirectoryInfo(dir);
        if (!fileDire.Exists)
        {
            throw new FileNotFoundException("目录:" + fileDire.FullName + "没有找到!");
        }
        GetAllDirFiles(fileDire, filesList);
        GetAllDirsFiles(fileDire.GetDirectories(), filesList);
        return filesList;
    }

    ///
    /// 获取一个文件夹下的所有文件夹里的文件
    ///
    ///
    ///
    private static void GetAllDirsFiles(DirectoryInfo[] dirs, Dictionary<string, DateTime> filesList)
    {
        foreach (var dir in dirs)
        {
            foreach (var file in dir.GetFiles("."))
            {
                filesList.Add(file.FullName, file.LastWriteTime);
            }
            GetAllDirsFiles(dir.GetDirectories(), filesList);
        }
    }

    ///
    /// 获取一个文件夹下的文件
    ///
    /// 目录名称
    /// 文件列表HastTable
    private static void GetAllDirFiles(DirectoryInfo dir, Dictionary<string, DateTime> filesList)
    {
        foreach (var file in dir.GetFiles())
        {
            filesList.Add(file.FullName, file.LastWriteTime);
        }
    }
}

/// <summary>
/// 文件对象
/// </summary>
public class FileItem
{
    /// <summary>
    /// 文件名称
    /// </summary>
    public string FileName { get; set; }

    /// <summary>
    /// 文件路径
    /// </summary>
    public string FilePath { get; set; }
}
