558 lines
23 KiB
C#
558 lines
23 KiB
C#
using ICSharpCode.SharpZipLib.Zip;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using UnityEngine;
|
||
|
||
public class ZipWrapper : MonoBehaviour
|
||
{
|
||
#region ZipCallback
|
||
public abstract class ZipCallback
|
||
{
|
||
/// <summary>
|
||
/// ѹ<><D1B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>ǰִ<C7B0>еĻص<C4BB>
|
||
/// </summary>
|
||
/// <param name="_entry"></param>
|
||
/// <returns><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>true<75><65><EFBFBD><EFBFBD>ѹ<EFBFBD><D1B9><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>У<EFBFBD><D0A3><EFBFBD>֮<EFBFBD><D6AE>ѹ<EFBFBD><D1B9><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD></returns>
|
||
public virtual bool OnPreZip(ZipEntry _entry)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// ѹ<><D1B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>к<EFBFBD>ִ<EFBFBD>еĻص<C4BB>
|
||
/// </summary>
|
||
/// <param name="_entry"></param>
|
||
public virtual void OnPostZip(ZipEntry _entry) { }
|
||
|
||
/// <summary>
|
||
/// ѹ<><D1B9>ִ<EFBFBD><D6B4><EFBFBD><EFBFBD>Ϻ<EFBFBD>Ļص<C4BB>
|
||
/// </summary>
|
||
/// <param name="_result">true<75><65>ʾѹ<CABE><D1B9><EFBFBD>ɹ<EFBFBD><C9B9><EFBFBD>false<73><65>ʾѹ<CABE><D1B9>ʧ<EFBFBD><CAA7></param>
|
||
public virtual void OnFinished(bool _result) { }
|
||
}
|
||
#endregion
|
||
|
||
#region UnzipCallback
|
||
public abstract class UnzipCallback
|
||
{
|
||
/// <summary>
|
||
/// <20><>ѹ<EFBFBD><D1B9><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>ǰִ<C7B0>еĻص<C4BB>
|
||
/// </summary>
|
||
/// <param name="_entry"></param>
|
||
/// <returns><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>true<75><65><EFBFBD><EFBFBD>ѹ<EFBFBD><D1B9><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>У<EFBFBD><D0A3><EFBFBD>֮<EFBFBD><D6AE>ѹ<EFBFBD><D1B9><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD></returns>
|
||
public virtual bool OnPreUnzip(ZipEntry _entry)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// <20><>ѹ<EFBFBD><D1B9><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>к<EFBFBD>ִ<EFBFBD>еĻص<C4BB>
|
||
/// </summary>
|
||
/// <param name="_entry"></param>
|
||
public virtual void OnPostUnzip(ZipEntry _entry) { }
|
||
|
||
/// <summary>
|
||
/// <20><>ѹִ<D1B9><D6B4><EFBFBD><EFBFBD>Ϻ<EFBFBD>Ļص<C4BB>
|
||
/// </summary>
|
||
/// <param name="_result">true<75><65>ʾ<EFBFBD><CABE>ѹ<EFBFBD>ɹ<EFBFBD><C9B9><EFBFBD>false<73><65>ʾ<EFBFBD><CABE>ѹʧ<D1B9><CAA7></param>
|
||
public virtual void OnFinished(bool _result) { }
|
||
}
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// ѹ<><D1B9><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>
|
||
/// </summary>
|
||
/// <param name="_fileOrDirectoryArray"><3E>ļ<EFBFBD><C4BC><EFBFBD>·<EFBFBD><C2B7><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD></param>
|
||
/// <param name="_outputPathName">ѹ<><D1B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD></param>
|
||
/// <param name="_password">ѹ<><D1B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
|
||
/// <param name="_zipCallback">ZipCallback<63><6B><EFBFBD><EFBFBD><F3A3ACB8><EFBFBD>ص<EFBFBD></param>
|
||
/// <returns></returns>
|
||
public static bool Zip(List<string> _fileOrDirectoryArray, string _outputPathName, string _password = null, ZipCallback _zipCallback = null)
|
||
{
|
||
if ((null == _fileOrDirectoryArray) || string.IsNullOrEmpty(_outputPathName))
|
||
{
|
||
if (null != _zipCallback)
|
||
_zipCallback.OnFinished(false);
|
||
|
||
return false;
|
||
}
|
||
|
||
// 使用 using 语句确保 FileStream 和 ZipOutputStream 资源被正确释放,即使在异常情况下也能释放
|
||
// 注意:Unity 平台不支持 System.Security.AccessControl,因此使用标准文件创建方式
|
||
// 在实际部署时,建议通过操作系统级别的文件权限设置来限制文件访问
|
||
using (FileStream fileStream = File.Create(_outputPathName))
|
||
{
|
||
using (ZipOutputStream zipOutputStream = new ZipOutputStream(fileStream))
|
||
{
|
||
zipOutputStream.SetLevel(6); // 压缩级别,压缩率与压缩速度的平衡点
|
||
if (!string.IsNullOrEmpty(_password))
|
||
zipOutputStream.Password = _password;
|
||
|
||
for (int index = 0; index < _fileOrDirectoryArray.Count; ++index)
|
||
{
|
||
bool result = false;
|
||
string fileOrDirectory = _fileOrDirectoryArray[index];
|
||
if (Directory.Exists(fileOrDirectory))
|
||
result = ZipDirectory(fileOrDirectory, string.Empty, zipOutputStream, _zipCallback);
|
||
else if (File.Exists(fileOrDirectory))
|
||
result = ZipFile(fileOrDirectory, string.Empty, zipOutputStream, _zipCallback);
|
||
|
||
if (!result)
|
||
{
|
||
if (null != _zipCallback)
|
||
_zipCallback.OnFinished(false);
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
zipOutputStream.Finish();
|
||
zipOutputStream.Close();
|
||
|
||
if (null != _zipCallback)
|
||
_zipCallback.OnFinished(true);
|
||
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// <20><>ѹZip<69><70>
|
||
/// </summary>
|
||
/// <param name="_filePathName">Zip<69><70><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>·<EFBFBD><C2B7><EFBFBD><EFBFBD></param>
|
||
/// <param name="_outputPath"><3E><>ѹ<EFBFBD><D1B9><EFBFBD>·<EFBFBD><C2B7></param>
|
||
/// <param name="_password"><3E><>ѹ<EFBFBD><D1B9><EFBFBD><EFBFBD></param>
|
||
/// <param name="_unzipCallback">UnzipCallback<63><6B><EFBFBD><EFBFBD><F3A3ACB8><EFBFBD>ص<EFBFBD></param>
|
||
/// <returns></returns>
|
||
public static bool UnzipFile(string _filePathName, string _outputPath, string _password = null, UnzipCallback _unzipCallback = null)
|
||
{
|
||
if (string.IsNullOrEmpty(_filePathName) || string.IsNullOrEmpty(_outputPath))
|
||
{
|
||
if (null != _unzipCallback)
|
||
_unzipCallback.OnFinished(false);
|
||
|
||
return false;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 使用 using 语句确保 FileStream 资源被正确释放
|
||
using (FileStream fileStream = File.OpenRead(_filePathName))
|
||
{
|
||
return UnzipFile(fileStream, _outputPath, _password, _unzipCallback);
|
||
}
|
||
}
|
||
catch (System.Exception _e)
|
||
{
|
||
Debug.LogError("[ZipUtility.UnzipFile]: " + _e.ToString());
|
||
|
||
if (null != _unzipCallback)
|
||
_unzipCallback.OnFinished(false);
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// <20><>ѹZip<69><70>
|
||
/// </summary>
|
||
/// <param name="_fileBytes">Zip<69><70><EFBFBD>ֽ<EFBFBD><D6BD><EFBFBD><EFBFBD><EFBFBD></param>
|
||
/// <param name="_outputPath"><3E><>ѹ<EFBFBD><D1B9><EFBFBD>·<EFBFBD><C2B7></param>
|
||
/// <param name="_password"><3E><>ѹ<EFBFBD><D1B9><EFBFBD><EFBFBD></param>
|
||
/// <param name="_unzipCallback">UnzipCallback<63><6B><EFBFBD><EFBFBD><F3A3ACB8><EFBFBD>ص<EFBFBD></param>
|
||
/// <returns></returns>
|
||
public static bool UnzipFile(byte[] _fileBytes, string _outputPath, string _password = null, UnzipCallback _unzipCallback = null)
|
||
{
|
||
if ((null == _fileBytes) || string.IsNullOrEmpty(_outputPath))
|
||
{
|
||
if (null != _unzipCallback)
|
||
_unzipCallback.OnFinished(false);
|
||
|
||
return false;
|
||
}
|
||
|
||
bool result = UnzipFile(new MemoryStream(_fileBytes), _outputPath, _password, _unzipCallback);
|
||
if (!result)
|
||
{
|
||
if (null != _unzipCallback)
|
||
_unzipCallback.OnFinished(false);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清理和验证 ZIP 条目名称,防止路径遍历攻击
|
||
/// </summary>
|
||
/// <param name="entryName">ZIP 条目名称(可能包含路径遍历字符)</param>
|
||
/// <param name="outputPath">输出目录路径</param>
|
||
/// <returns>清理后的安全路径,如果路径不安全则返回 null</returns>
|
||
private static string SanitizeZipEntryName(string entryName, string outputPath)
|
||
{
|
||
if (string.IsNullOrEmpty(entryName))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
// 规范化路径分隔符(统一使用正斜杠或反斜杠)
|
||
string normalizedName = entryName.Replace('\\', '/');
|
||
|
||
// 移除路径遍历字符(.. 和 .)
|
||
string[] parts = normalizedName.Split('/');
|
||
List<string> safeParts = new List<string>();
|
||
|
||
foreach (string part in parts)
|
||
{
|
||
if (string.IsNullOrEmpty(part) || part == ".")
|
||
{
|
||
// 跳过空字符串和当前目录引用
|
||
continue;
|
||
}
|
||
else if (part == "..")
|
||
{
|
||
// 如果遇到父目录引用,移除最后一个安全部分(如果存在)
|
||
if (safeParts.Count > 0)
|
||
{
|
||
safeParts.RemoveAt(safeParts.Count - 1);
|
||
}
|
||
else
|
||
{
|
||
// 如果已经在根目录,则忽略路径遍历尝试
|
||
// 这表示恶意路径遍历,返回 null 表示拒绝
|
||
return null;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 验证路径片段是否安全:检查是否包含路径遍历字符或绝对路径标识符
|
||
// 防止恶意构造的路径片段(如 "C:" 或包含 ".." 的片段)
|
||
if (part.Contains("..") || part.Contains("\\") || part.Contains(":"))
|
||
{
|
||
Debug.LogWarning($"[ZipWrapper.SanitizeZipEntryName] 检测到不安全的路径片段: {part}");
|
||
return null;
|
||
}
|
||
|
||
// 验证路径片段是否为绝对路径(Windows 驱动器号)
|
||
if (part.Length >= 2 && part[1] == ':' && part[0] >= 'A' && part[0] <= 'Z')
|
||
{
|
||
Debug.LogWarning($"[ZipWrapper.SanitizeZipEntryName] 检测到绝对路径片段(驱动器号): {part}");
|
||
return null;
|
||
}
|
||
|
||
// 添加到安全部分列表
|
||
safeParts.Add(part);
|
||
}
|
||
}
|
||
|
||
if (safeParts.Count == 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
// 重新组合安全路径
|
||
string safePath = string.Join(Path.DirectorySeparatorChar.ToString(), safeParts.ToArray());
|
||
|
||
// 额外验证:确保 safePath 不包含路径遍历字符(防御性检查)
|
||
// 检查是否包含路径遍历字符或绝对路径标识符
|
||
if (safePath.Contains("..") || (safePath.Length >= 2 && safePath[1] == ':' && safePath[0] >= 'A' && safePath[0] <= 'Z'))
|
||
{
|
||
Debug.LogWarning($"[ZipWrapper.SanitizeZipEntryName] 检测到不安全的路径片段: {safePath}");
|
||
return null;
|
||
}
|
||
|
||
// 在 Path.Combine 之前,额外验证 safePath 是否为绝对路径
|
||
// 如果 safePath 是绝对路径,则拒绝,防止路径遍历攻击
|
||
if (Path.IsPathRooted(safePath))
|
||
{
|
||
Debug.LogWarning($"[ZipWrapper.SanitizeZipEntryName] 检测到绝对路径,拒绝路径遍历攻击: {entryName} -> {safePath}");
|
||
return null;
|
||
}
|
||
|
||
// 与输出路径组合(使用已清理的安全路径)
|
||
string fullPath = Path.Combine(outputPath, safePath);
|
||
|
||
// 规范化最终路径
|
||
fullPath = Path.GetFullPath(fullPath);
|
||
string normalizedOutputPath = Path.GetFullPath(outputPath);
|
||
|
||
// 验证最终路径是否在输出目录内(防止路径遍历攻击)
|
||
if (!fullPath.StartsWith(normalizedOutputPath + Path.DirectorySeparatorChar, System.StringComparison.Ordinal)
|
||
&& fullPath != normalizedOutputPath)
|
||
{
|
||
// 路径不在预期目录内,拒绝访问
|
||
Debug.LogWarning($"[ZipWrapper.SanitizeZipEntryName] 检测到路径遍历攻击尝试: {entryName} -> {fullPath}");
|
||
return null;
|
||
}
|
||
|
||
return fullPath;
|
||
}
|
||
|
||
/// <summary>
|
||
/// <20><>ѹZip<69><70>
|
||
/// </summary>
|
||
/// <param name="_inputStream">Zip<69><70><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
|
||
/// <param name="_outputPath"><3E><>ѹ<EFBFBD><D1B9><EFBFBD>·<EFBFBD><C2B7></param>
|
||
/// <param name="_password"><3E><>ѹ<EFBFBD><D1B9><EFBFBD><EFBFBD></param>
|
||
/// <param name="_unzipCallback">UnzipCallback<63><6B><EFBFBD><EFBFBD><F3A3ACB8><EFBFBD>ص<EFBFBD></param>
|
||
/// <returns></returns>
|
||
public static bool UnzipFile(Stream _inputStream, string _outputPath, string _password = null, UnzipCallback _unzipCallback = null)
|
||
{
|
||
if ((null == _inputStream) || string.IsNullOrEmpty(_outputPath))
|
||
{
|
||
if (null != _unzipCallback)
|
||
_unzipCallback.OnFinished(false);
|
||
|
||
return false;
|
||
}
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>Ŀ¼
|
||
if (!Directory.Exists(_outputPath))
|
||
Directory.CreateDirectory(_outputPath);
|
||
|
||
// <20><>ѹZip<69><70>
|
||
ZipEntry entry = null;
|
||
using (ZipInputStream zipInputStream = new ZipInputStream(_inputStream))
|
||
{
|
||
if (!string.IsNullOrEmpty(_password))
|
||
zipInputStream.Password = _password;
|
||
|
||
while (null != (entry = zipInputStream.GetNextEntry()))
|
||
{
|
||
if (string.IsNullOrEmpty(entry.Name))
|
||
continue;
|
||
|
||
if ((null != _unzipCallback) && !_unzipCallback.OnPreUnzip(entry))
|
||
continue; // <20><><EFBFBD><EFBFBD>
|
||
|
||
// 使用安全验证方法清理和验证 ZIP 条目名称,防止路径遍历攻击
|
||
string filePathName = SanitizeZipEntryName(entry.Name, _outputPath);
|
||
if (filePathName == null)
|
||
{
|
||
// 路径不安全,跳过此条目
|
||
Debug.LogWarning($"[ZipWrapper.UnzipFile] 跳过不安全的 ZIP 条目: {entry.Name}");
|
||
continue;
|
||
}
|
||
|
||
// 额外的安全验证:在使用 filePathName 创建文件前,再次验证路径的安全性
|
||
// 确保 filePathName 是规范化后的绝对路径,且确实在输出目录内
|
||
string normalizedFilePath = Path.GetFullPath(filePathName);
|
||
string normalizedOutputPath = Path.GetFullPath(_outputPath);
|
||
|
||
// 验证路径是否在输出目录内(防止路径遍历攻击)
|
||
if (!normalizedFilePath.StartsWith(normalizedOutputPath + Path.DirectorySeparatorChar, System.StringComparison.OrdinalIgnoreCase)
|
||
&& normalizedFilePath != normalizedOutputPath)
|
||
{
|
||
Debug.LogWarning($"[ZipWrapper.UnzipFile] 检测到路径遍历攻击,拒绝访问: {entry.Name} -> {normalizedFilePath}");
|
||
continue;
|
||
}
|
||
|
||
// 确保路径中不包含不安全的字符
|
||
if (normalizedFilePath.Contains("..") || Path.IsPathRooted(entry.Name))
|
||
{
|
||
Debug.LogWarning($"[ZipWrapper.UnzipFile] 检测到不安全的路径字符,拒绝访问: {entry.Name}");
|
||
continue;
|
||
}
|
||
|
||
// 创建父目录(如果需要)
|
||
string directoryPath = Path.GetDirectoryName(normalizedFilePath);
|
||
if (!string.IsNullOrEmpty(directoryPath))
|
||
{
|
||
// 再次验证父目录路径的安全性,确保在输出目录内
|
||
string normalizedDirectoryPath = Path.GetFullPath(directoryPath);
|
||
if (normalizedDirectoryPath.StartsWith(normalizedOutputPath + Path.DirectorySeparatorChar, System.StringComparison.OrdinalIgnoreCase)
|
||
|| normalizedDirectoryPath == normalizedOutputPath)
|
||
{
|
||
if (!Directory.Exists(normalizedDirectoryPath))
|
||
{
|
||
Directory.CreateDirectory(normalizedDirectoryPath);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"[ZipWrapper.UnzipFile] 父目录路径不在输出目录内,拒绝创建: {directoryPath}");
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// 如果是目录条目,创建目录后跳过
|
||
if (entry.IsDirectory)
|
||
{
|
||
// 再次验证目录路径的安全性,确保在输出目录内
|
||
if (normalizedFilePath.StartsWith(normalizedOutputPath + Path.DirectorySeparatorChar, System.StringComparison.OrdinalIgnoreCase)
|
||
|| normalizedFilePath == normalizedOutputPath)
|
||
{
|
||
Directory.CreateDirectory(normalizedFilePath);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"[ZipWrapper.UnzipFile] 目录路径不在输出目录内,拒绝创建: {normalizedFilePath}");
|
||
}
|
||
continue;
|
||
}
|
||
|
||
// 写入文件
|
||
try
|
||
{
|
||
// 最终安全验证:在使用 File.Create 前,最后一次验证路径的安全性
|
||
// 确保 normalizedFilePath 仍然是规范化的绝对路径,且在输出目录内
|
||
string finalFilePath = Path.GetFullPath(normalizedFilePath);
|
||
string finalOutputPath = Path.GetFullPath(_outputPath);
|
||
|
||
// 再次验证路径是否在输出目录内(防止路径遍历攻击)
|
||
if (!finalFilePath.StartsWith(finalOutputPath + Path.DirectorySeparatorChar, System.StringComparison.OrdinalIgnoreCase)
|
||
&& finalFilePath != finalOutputPath)
|
||
{
|
||
Debug.LogWarning($"[ZipWrapper.UnzipFile] 最终验证失败:路径不在输出目录内,拒绝创建文件: {entry.Name} -> {finalFilePath}");
|
||
continue;
|
||
}
|
||
|
||
// 确保路径中不包含路径遍历字符
|
||
if (finalFilePath.Contains(".."))
|
||
{
|
||
Debug.LogWarning($"[ZipWrapper.UnzipFile] 最终验证失败:检测到路径遍历字符,拒绝创建文件: {entry.Name}");
|
||
continue;
|
||
}
|
||
|
||
// 使用规范化后的安全路径创建文件
|
||
// 注意:Unity 平台不支持 System.Security.AccessControl,因此使用标准文件创建方式
|
||
// 在实际部署时,建议通过操作系统级别的文件权限设置来限制文件访问
|
||
using (FileStream fileStream = File.Create(finalFilePath))
|
||
{
|
||
byte[] bytes = new byte[1024];
|
||
while (true)
|
||
{
|
||
int count = zipInputStream.Read(bytes, 0, bytes.Length);
|
||
if (count > 0)
|
||
fileStream.Write(bytes, 0, count);
|
||
else
|
||
{
|
||
if (null != _unzipCallback)
|
||
_unzipCallback.OnPostUnzip(entry);
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (System.Exception _e)
|
||
{
|
||
Debug.LogError("[ZipUtility.UnzipFile]: " + _e.ToString());
|
||
|
||
if (null != _unzipCallback)
|
||
_unzipCallback.OnFinished(false);
|
||
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (null != _unzipCallback)
|
||
_unzipCallback.OnFinished(true);
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// ѹ<><D1B9><EFBFBD>ļ<EFBFBD>
|
||
/// </summary>
|
||
/// <param name="_filePathName"><3E>ļ<EFBFBD>·<EFBFBD><C2B7><EFBFBD><EFBFBD></param>
|
||
/// <param name="_parentRelPath">Ҫѹ<D2AA><D1B9><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>ĸ<EFBFBD><C4B8><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD></param>
|
||
/// <param name="_zipOutputStream">ѹ<><D1B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
|
||
/// <param name="_zipCallback">ZipCallback<63><6B><EFBFBD><EFBFBD><F3A3ACB8><EFBFBD>ص<EFBFBD></param>
|
||
/// <returns></returns>
|
||
private static bool ZipFile(string _filePathName, string _parentRelPath, ZipOutputStream _zipOutputStream, ZipCallback _zipCallback = null)
|
||
{
|
||
|
||
ZipEntry entry = null;
|
||
try
|
||
{
|
||
string entryName = _parentRelPath + '/' + Path.GetFileName(_filePathName);
|
||
entry = new ZipEntry(entryName);
|
||
entry.DateTime = System.DateTime.Now;
|
||
|
||
|
||
|
||
|
||
// 使用 using 语句确保 FileStream 资源被正确释放
|
||
using (FileStream fileStream = File.OpenRead(_filePathName))
|
||
{
|
||
byte[] buffer = new byte[fileStream.Length];
|
||
fileStream.Read(buffer, 0, buffer.Length);
|
||
|
||
entry.Size = buffer.Length;
|
||
|
||
_zipOutputStream.PutNextEntry(entry);
|
||
_zipOutputStream.Write(buffer, 0, buffer.Length);
|
||
}
|
||
}
|
||
catch (System.Exception _e)
|
||
{
|
||
Debug.LogError("[ZipUtility.ZipFile]: " + _e.ToString());
|
||
return false;
|
||
}
|
||
|
||
if (null != _zipCallback)
|
||
_zipCallback.OnPostZip(entry);
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// ѹ<><D1B9><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>
|
||
/// </summary>
|
||
/// <param name="_path">Ҫѹ<D2AA><D1B9><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD></param>
|
||
/// <param name="_parentRelPath">Ҫѹ<D2AA><D1B9><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>еĸ<D0B5><C4B8><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD></param>
|
||
/// <param name="_zipOutputStream">ѹ<><D1B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
|
||
/// <param name="_zipCallback">ZipCallback<63><6B><EFBFBD><EFBFBD><F3A3ACB8><EFBFBD>ص<EFBFBD></param>
|
||
/// <returns></returns>
|
||
private static bool ZipDirectory(string _path, string _parentRelPath, ZipOutputStream _zipOutputStream, ZipCallback _zipCallback = null)
|
||
{
|
||
ZipEntry entry = null;
|
||
try
|
||
{
|
||
string entryName = Path.Combine(_parentRelPath, Path.GetFileName(_path) + '/');
|
||
entry = new ZipEntry(entryName);
|
||
entry.DateTime = System.DateTime.Now;
|
||
entry.Size = 0;
|
||
|
||
|
||
|
||
|
||
_zipOutputStream.PutNextEntry(entry);
|
||
_zipOutputStream.Flush();
|
||
|
||
string[] files = Directory.GetFiles(_path);
|
||
for (int index = 0; index < files.Length; ++index)
|
||
{
|
||
// <20>ų<EFBFBD>Unity<74>п<EFBFBD><D0BF>ܵ<EFBFBD> .meta <20>ļ<EFBFBD>
|
||
if (files[index].EndsWith(".meta") == true)
|
||
{
|
||
Debug.LogWarning(files[index] + " not to zip");
|
||
continue;
|
||
}
|
||
|
||
ZipFile(files[index], Path.Combine(_parentRelPath, Path.GetFileName(_path)), _zipOutputStream, _zipCallback);
|
||
}
|
||
}
|
||
catch (System.Exception _e)
|
||
{
|
||
Debug.LogError("[ZipUtility.ZipDirectory]: " + _e.ToString());
|
||
return false;
|
||
}
|
||
|
||
string[] directories = Directory.GetDirectories(_path);
|
||
for (int index = 0; index < directories.Length; ++index)
|
||
{
|
||
if (!ZipDirectory(directories[index], Path.Combine(_parentRelPath, Path.GetFileName(_path)), _zipOutputStream, _zipCallback))
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (null != _zipCallback)
|
||
_zipCallback.OnPostZip(entry);
|
||
|
||
return true;
|
||
}
|
||
} |