Files
plugin-library/Assets/00.StaryEvoTools/Runtime/Tools/CrossPlatformUnzip.cs
stary f8ca656870
All checks were successful
Plugin Library CI / publish (00.StaryEvo) (push) Successful in 3s
Plugin Library CI / publish (00.BuildOriginality) (push) Successful in 3s
Plugin Library CI / publish (00.StaryEvoTools) (push) Successful in 19s
Plugin Library CI / publish (01.HybridCLR) (push) Successful in 5s
Plugin Library CI / publish (02.InformationSave) (push) Successful in 3s
Plugin Library CI / publish (03.YooAsset) (push) Successful in 32s
Plugin Library CI / publish (04.AudioCore) (push) Successful in 3s
Plugin Library CI / publish (05.TableTextConversion) (push) Successful in 4s
Plugin Library CI / publish (06.UIFarme) (push) Successful in 16s
Plugin Library CI / publish (07.RKTools) (push) Successful in 3s
Plugin Library CI / publish (08.UniTask) (push) Successful in 3s
Plugin Library CI / publish (09.CodeChecker) (push) Successful in 16s
Plugin Library CI / publish (10.StoryEditor) (push) Successful in 3s
Plugin Library CI / publish (10.XNode) (push) Successful in 3s
Plugin Library CI / publish (11.PointCloudTools) (push) Successful in 3s
1.4.5报错版本修改
2026-04-16 22:26:36 +08:00

317 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using UnityEngine;
using Cysharp.Threading.Tasks;
#if WECHAT_MINIGAME
using WeChatWASM;
#else
using System.IO; // 仅在非微信平台使用
using ICSharpCode.SharpZipLib.Zip;
#endif
namespace Stary.Evo.Unzip
{
public static class CrossPlatformUnzip
{
public static async UniTask UnzipAsync(string zipPath, string extractPath,
Action<float> progressCallback = null, bool deleteAfterExtract = true)
{
#if WECHAT_MINIGAME && UNITY_WEBGL
// 微信小程序:使用原生 WX API零 System.IO
await UnzipWeChatNativeAsync(zipPath, extractPath, progressCallback, deleteAfterExtract);
#elif UNITY_WEBGL && !WECHAT_MINIGAME
// WebGL使用 SharpZipLib虚拟文件系统
await UnzipWebGLAsync(zipPath, extractPath, progressCallback, deleteAfterExtract);
#else
// PC/移动端:标准 System.IO
await UnzipStandardAsync(zipPath, extractPath, progressCallback, deleteAfterExtract);
#endif
}
#region 1. System.IO
#if WECHAT_MINIGAME
private static async UniTask UnzipWeChatNativeAsync(string zipPath, string extractPath,
Action<float> progressCallback, bool deleteAfterExtract)
{
var tcs = new UniTaskCompletionSource();
var fs = WX.GetFileSystemManager();
// 检查 ZIP 存在(使用微信 API
bool fileExists = false;
fs.Access(new AccessParam()
{
path = zipPath,
success = (res) => fileExists = true,
fail = (err) => { }
});
await UniTask.Delay(50); // 等待异步结果
if (!fileExists)
throw new Exception($"ZIP 不存在: {zipPath}");
// 清理目标目录(递归删除)
try
{
fs.RmdirSync(extractPath, true);
}
catch
{
// 目录不存在或已删除
}
// 创建目标目录
fs.MkdirSync(extractPath, true);
// 模拟进度WX.Unzip 无细粒度进度)
float reportedProgress = 0f;
var progressTask = SimulateProgressAsync(progressCallback, () => reportedProgress >= 1f);
// 微信原生解压性能最佳C++ 实现)
WX.GetFileSystemManager().Unzip(new UnzipOption()
{
zipFilePath = zipPath,
targetPath = extractPath,
success = (res) =>
{
reportedProgress = 1f;
progressCallback?.Invoke(1f);
tcs.TrySetResult();
},
fail = (err) => { tcs.TrySetException(new Exception($"微信解压失败: {err.errMsg}")); }
});
await tcs.Task;
await progressTask;
// 删除 ZIP 释放空间(微信 200MB 限制严格)
if (deleteAfterExtract)
{
DeleteWeChatFileSafe(fs, zipPath);
}
}
private static async UniTask SimulateProgressAsync(Action<float> callback, Func<bool> isCompleted)
{
if (callback == null) return;
float progress = 0f;
while (!isCompleted())
{
progress = Mathf.Min(progress + UnityEngine.Random.Range(0.02f, 0.08f), 0.95f);
callback.Invoke(progress);
await UniTask.Delay(200);
}
}
private static void DeleteWeChatFileSafe(WXFileSystemManager fs, string path)
{
try
{
fs.UnlinkSync(path);
Debug.Log($"[微信] 已删除 ZIP");
}
catch (Exception ex)
{
Debug.LogWarning($"[微信] 删除失败: {ex.Message}");
}
}
/// <summary>
/// 获取微信缓存路径(字符串拼接,不使用 Path.Combine
/// </summary>
public static string GetWeChatPath(string relativePath)
{
// 手动拼接路径,避免使用 System.IO.Path
string basePath = WX.env.USER_DATA_PATH;
if (relativePath.StartsWith("/"))
return basePath + relativePath;
return basePath + "/" + relativePath;
}
#endif
#endregion
#region 2. WebGL SharpZipLib +
#if UNITY_WEBGL && !WECHAT_MINIGAME
private static async UniTask UnzipWebGLAsync(string zipPath, string extractPath,
Action<float> progressCallback, bool deleteAfterExtract)
{
// WebGL 使用 Unity 的虚拟文件系统(基于 IndexedDB
// 注意:虽然是 FileStream但在 WebGL 中实际是虚拟 IO
if (!File.Exists(zipPath))
throw new FileNotFoundException($"ZIP 不存在: {zipPath}");
string fullExtractPath = Path.GetFullPath(extractPath);
// 清理目录
if (Directory.Exists(extractPath))
Directory.Delete(extractPath, true);
Directory.CreateDirectory(extractPath);
using (FileStream fs = File.OpenRead(zipPath))
using (ZipFile zipFile = new ZipFile(fs))
{
int total = 0;
var validEntries = new List<ZipEntry>();
foreach (ZipEntry entry in zipFile)
{
if (entry.IsFile && !string.IsNullOrEmpty(entry.Name))
{
validEntries.Add(entry);
total++;
}
}
// WebGL 分帧解压,避免卡死浏览器
int processed = 0;
const int FILES_PER_FRAME = 3;
foreach (var entry in validEntries)
{
// 安全检查
string fullPath = Path.GetFullPath(Path.Combine(extractPath, entry.Name));
if (!fullPath.StartsWith(fullExtractPath))
{
Debug.LogWarning($"跳过非法路径: {entry.Name}");
continue;
}
// 创建目录
string dir = Path.GetDirectoryName(fullPath);
if (!string.IsNullOrEmpty(dir))
Directory.CreateDirectory(dir);
// 解压单文件
using (Stream input = zipFile.GetInputStream(entry))
using (FileStream output = File.Create(fullPath))
{
byte[] buffer = new byte[4096]; // WebGL 使用更小缓冲区
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
}
processed++;
// 每 N 个文件让出一帧,保持浏览器响应
if (processed % FILES_PER_FRAME == 0)
{
progressCallback?.Invoke((float)processed / total);
await UniTask.Yield();
}
}
}
if (deleteAfterExtract)
{
File.Delete(zipPath);
}
}
#endif
#endregion
#region 3. PC/ System.IO
#if !UNITY_WEBGL && !WECHAT_MINIGAME
private static async UniTask UnzipStandardAsync(string zipPath, string extractPath,
Action<float> progressCallback, bool deleteAfterExtract)
{
await UniTask.SwitchToThreadPool();
try
{
if (!File.Exists(zipPath))
throw new FileNotFoundException($"ZIP 不存在: {zipPath}");
string fullExtractPath = Path.GetFullPath(extractPath);
if (Directory.Exists(extractPath))
Directory.Delete(extractPath, true);
Directory.CreateDirectory(extractPath);
using (FileStream fs = File.OpenRead(zipPath))
using (ZipFile zipFile = new ZipFile(fs))
{
int total = 0;
foreach (ZipEntry entry in zipFile)
if (entry.IsFile)
total++;
int processed = 0;
foreach (ZipEntry entry in zipFile)
{
if (!entry.IsFile) continue;
string fullPath = Path.GetFullPath(Path.Combine(extractPath, entry.Name));
if (!fullPath.StartsWith(fullExtractPath)) continue;
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
using (Stream input = zipFile.GetInputStream(entry))
using (FileStream output = File.Create(fullPath))
{
await input.CopyToAsync(output);
}
processed++;
await UniTask.SwitchToMainThread();
progressCallback?.Invoke((float)processed / total);
await UniTask.SwitchToThreadPool();
}
}
}
finally
{
await UniTask.SwitchToMainThread();
if (deleteAfterExtract && File.Exists(zipPath))
File.Delete(zipPath);
}
}
#endif
#endregion
#region 4.
/// <summary>
/// 获取缓存路径(平台特定实现)
/// </summary>
public static string GetCachePath(string relativePath)
{
#if WECHAT_MINIGAME
return GetWeChatPath(relativePath);
#else
return Path.Combine(Application.persistentDataPath, relativePath);
#endif
}
/// <summary>
/// 检查文件存在(平台特定)
/// </summary>
public static bool FileExists(string path)
{
#if WECHAT_MINIGAME
bool exists = false;
WX.GetFileSystemManager().Access(new AccessParam()
{
path = path,
success = (res) => exists = true,
fail = (err) => { }
});
return exists;
#else
return File.Exists(path);
#endif
}
#endregion
}
}