248 lines
12 KiB
C#
248 lines
12 KiB
C#
using Cysharp.Threading.Tasks;
|
|
using System;
|
|
|
|
namespace UVC.Util
|
|
{
|
|
/// <summary>
|
|
/// Zipper 클래스는 압축 파일(Zip 및 7z)의 압축 해제 기능을 제공합니다.
|
|
/// 7zip 라이브러리를 사용하며, 압축 해제 진행률을 실시간으로 추적할 수 있습니다.
|
|
/// 7zip 사용. https://assetstore.unity.com/packages/tools/input-management/7zip-lzma-lz4-fastlz-zip-gzip-brotli-multiplatform-plugins-12674
|
|
/// </summary>
|
|
public class Zipper
|
|
{
|
|
|
|
// zip 보관 파일을 압축하지 않은 현재 파일 수로 변경하는 단일 항목 정수 배열입니다.
|
|
// decompress_File 기능을 실행할 때 getTotalFiles 기능으로 반환된 총 파일 수와 이 int를 비교하여 zip에 여러 개의 파일이 포함된 경우 추출의 진행 상황을 파악한다.
|
|
// 여러 개의 스레드를 사용하는 경우 다른 스레드에 다른 진행 정수를 사용해야 합니다.그렇지 않으면 공유 위반이 발생합니다.
|
|
private int[] progress;
|
|
|
|
/// <summary>
|
|
/// 개별 파일의 압축 해제 진행률(바이트 단위)을 나타내는 배열입니다.
|
|
/// </summary>
|
|
private ulong[] progress2;
|
|
/// <summary>
|
|
/// 압축 파일의 경로를 저장합니다.
|
|
/// </summary>
|
|
private string zipFilePath;
|
|
/// <summary>
|
|
/// 압축 해제가 완료되었는지 여부를 나타냅니다.
|
|
/// </summary>
|
|
private bool isComplete = false;
|
|
/// <summary>
|
|
/// 압축 해제 진행률(0.0 ~ 1.0)을 나타냅니다.
|
|
/// </summary>
|
|
private float percent = 0;
|
|
/// <summary>
|
|
/// 압축 해제 진행 상황을 콜백으로 전달하는 액션입니다.
|
|
/// </summary>
|
|
private Action<long, long, float> OnProgress;
|
|
|
|
|
|
/// <summary>
|
|
/// 압축 파일을 해제합니다.
|
|
/// 파일 형식(Zip 또는 7z)에 따라 적절한 메서드를 호출합니다.
|
|
/// </summary>
|
|
/// <param name="zipFilePath">압축 파일 경로</param>
|
|
/// <param name="decompressFolderPath">압축 해제 대상 폴더 경로</param>
|
|
/// <param name="OnProgress">압축 해제 진행 상황을 전달하는 콜백</param>
|
|
/// <returns>압축 해제 결과 메시지</returns>
|
|
/// <example>
|
|
/// Zipper zipper = new Zipper();
|
|
/// string result = await zipper.Decompress("path/to/file.zip", "path/to/output", (current, total, percent) =>
|
|
/// {
|
|
/// Debug.Log($"Progress: {percent * 100}%");
|
|
/// });
|
|
/// if (result == null)
|
|
/// {
|
|
/// Debug.Log("Decompression successful!");
|
|
/// }
|
|
/// else
|
|
/// {
|
|
/// Debug.LogError($"Decompression failed: {result}");
|
|
/// }
|
|
/// </example>
|
|
public async UniTask<string> Decompress(string zipFilePath, string decompressFolderPath, Action<long, long, float> OnProgress)
|
|
{
|
|
if (zipFilePath.ToLower().Contains(".zip"))
|
|
{
|
|
return await DecompressZip(zipFilePath, decompressFolderPath, OnProgress);
|
|
}
|
|
else if (zipFilePath.ToLower().Contains(".7z"))
|
|
{
|
|
return await Decompress7Zip(zipFilePath, decompressFolderPath, OnProgress);
|
|
}
|
|
return "Unsupported compressed file";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zip 파일을 압축 해제합니다.
|
|
/// </summary>
|
|
/// <param name="zipFilePath">압축 파일 경로</param>
|
|
/// <param name="decompressFolderPath">압축 해제 대상 폴더 경로</param>
|
|
/// <param name="OnProgress">압축 해제 진행 상황을 전달하는 콜백</param>
|
|
/// <returns>압축 해제 결과 메시지</returns>
|
|
/// <example>
|
|
/// Zipper zipper = new Zipper();
|
|
/// string result = await zipper.DecompressZip("path/to/file.zip", "path/to/output", (current, total, percent) =>
|
|
/// {
|
|
/// Debug.Log($"Progress: {percent * 100}%");
|
|
/// });
|
|
/// if (result == null)
|
|
/// {
|
|
/// Debug.Log("Decompression successful!");
|
|
/// }
|
|
/// else
|
|
/// {
|
|
/// Debug.LogError($"Decompression failed: {result}");
|
|
/// }
|
|
/// </example>
|
|
public async UniTask<string> DecompressZip(string zipFilePath, string decompressFolderPath, Action<long, long, float> OnProgress)
|
|
{
|
|
progress = new int[1];
|
|
progress2 = new ulong[1];
|
|
isComplete = false;
|
|
this.zipFilePath = zipFilePath;
|
|
this.OnProgress = OnProgress;
|
|
|
|
if (lzip.validateFile(zipFilePath))
|
|
{
|
|
//getFileInfo zip 내의 콘텐츠의 총 압축되지 않은 바이트를 반환한다.
|
|
ulong totalBytes = lzip.getFileInfo(zipFilePath);
|
|
CountPercentZipAsync(totalBytes).Forget();
|
|
int result = await UniTask.RunOnThreadPool(() =>
|
|
{
|
|
return lzip.decompress_File(zipFilePath, decompressFolderPath, progress, null, progress2);
|
|
});
|
|
isComplete = true;
|
|
if (result == 1) //success
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
//-1 = could not initialize zip archive.
|
|
//-2 = failed extraction
|
|
//- 6 = IntPtr buffer had no valid buffer length parameter passed.
|
|
string errorMessage = "Unknown error";
|
|
if (result == -1) errorMessage = "could not initialize zip archive.";
|
|
else if (result == -2) errorMessage = "failed extraction.";
|
|
else if (result == -6) errorMessage = "IntPtr buffer had no valid buffer length parameter passed.";
|
|
return errorMessage;
|
|
}
|
|
}
|
|
|
|
return "invalidate File";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zip 파일의 압축 해제 진행률을 계산합니다.
|
|
/// </summary>
|
|
/// <param name="totalBytes">압축 해제 대상 파일의 총 바이트 수</param>
|
|
private async UniTaskVoid CountPercentZipAsync(ulong totalBytes)
|
|
{
|
|
|
|
percent = (float)progress2[0] / (float)totalBytes;
|
|
|
|
//Debug.Log($"CountPercentAsync totalBytes:{totalBytes} progress:{progress[0]} progress2:{progress2[0]} {percent}");
|
|
if (OnProgress != null) OnProgress((long)progress2[0], (long)totalBytes, percent);
|
|
if (percent == 1) isComplete = true;
|
|
if (!isComplete)
|
|
{
|
|
await UniTask.Delay(TimeSpan.FromMilliseconds(100));//0.1초에 한번씩
|
|
CountPercentZipAsync(totalBytes).Forget();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 7z 파일을 압축 해제합니다.
|
|
/// </summary>
|
|
/// <param name="zipFilePath">압축 파일 경로</param>
|
|
/// <param name="decompressFolderPath">압축 해제 대상 폴더 경로</param>
|
|
/// <param name="OnProgress">압축 해제 진행 상황을 전달하는 콜백</param>
|
|
/// <returns>압축 해제 결과 메시지</returns>
|
|
/// <example>
|
|
/// Zipper zipper = new Zipper();
|
|
/// string result = await zipper.Decompress7Zip("path/to/file.7z", "path/to/output", (current, total, percent) =>
|
|
/// {
|
|
/// Debug.Log($"Progress: {percent * 100}%");
|
|
/// });
|
|
/// if (result == null)
|
|
/// {
|
|
/// Debug.Log("Decompression successful!");
|
|
/// }
|
|
/// else
|
|
/// {
|
|
/// Debug.LogError($"Decompression failed: {result}");
|
|
/// }
|
|
/// </example>
|
|
public async UniTask<string> Decompress7Zip(string zipFilePath, string decompressFolderPath, Action<long, long, float> OnProgress)
|
|
{
|
|
progress = new int[1] { 0 };
|
|
isComplete = false;
|
|
this.zipFilePath = zipFilePath;
|
|
this.OnProgress = OnProgress;
|
|
|
|
// 참조된 progress int는 압축 해제 중인 파일의 현재 인덱스를 나타냅니다. 별도의 스레드에서 사용하여 실시간으로 표시합니다.
|
|
// (thread에서) 실시간 바이트 레벨 압축해제 진행을 얻기 위해서는 두 가지 방법이 있습니다.
|
|
// 1. lzma.get7zSize 함수를 사용하여 파일의 총 압축되지 않은 크기를 구하고 lzma.getBytesWritten 함수를 호출하여 실시간으로 작성된 바이트들과 비교한다.
|
|
// 2. lzma.getFileSize(또는 FileBuffers의 버퍼 길이)를 사용하여 파일 크기를 구하고 lzma.getBytesRead 기능을 호출하여 실시간으로 읽는 바이트들과 비교한다.
|
|
//FileInfo fileInfo = new(zipFilePath);
|
|
//FileSecurity fsecurity = fileInfo.GetAccessControl();
|
|
//var id = System.Security.Principal.WindowsIdentity.GetCurrent().Name.Trim();
|
|
//var name = id.Split("\\");
|
|
//fsecurity.AddAccessRule(new FileSystemAccessRule(new NTAccount(name[1]), FileSystemRights.FullControl, InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow));
|
|
//fileInfo.SetAccessControl(fsecurity);
|
|
long totalBytes = lzma.getFileSize(zipFilePath);
|
|
CountPercent7ZipAsync(totalBytes, true).Forget();
|
|
int result = await UniTask.RunOnThreadPool(() =>
|
|
{
|
|
return lzma.doDecompress7zip(zipFilePath, decompressFolderPath, progress, true, true);
|
|
});
|
|
isComplete = true;
|
|
if (result == 1) //success
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
//1 : OK
|
|
//2 : Could not find requested file in archive
|
|
//- 1 : Could not open input(7z) file
|
|
//- 2 : Decoder doesn't support this archive
|
|
//- 3 : Can not allocate memory
|
|
//-5 : Unknown error
|
|
//-6 : File IO error
|
|
string errorMessage = "Unknown error";
|
|
if (result == 2) errorMessage = "Could not find requested file in archive";
|
|
else if (result == -1) errorMessage = "Could not open input(7z) file";
|
|
else if (result == -2) errorMessage = "Decoder doesn't support this archive";
|
|
else if (result == -3) errorMessage = "Can not allocate memory";
|
|
else if (result == -6) errorMessage = "File IO error";
|
|
return errorMessage;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 7z 파일의 압축 해제 진행률을 계산합니다.
|
|
/// </summary>
|
|
/// <param name="totalBytes">압축 해제 대상 파일의 총 바이트 수</param>
|
|
/// <param name="isFirst">첫 번째 호출 여부</param>
|
|
private async UniTaskVoid CountPercent7ZipAsync(long totalBytes, bool isFirst = false)
|
|
{
|
|
if (!isFirst)
|
|
{
|
|
long readByted = (long)lzma.getBytesRead();
|
|
percent = (float)readByted / (float)totalBytes;
|
|
//Debug.Log($"CountPercent7ZipAsync totalBytes:{totalBytes} progress:{progress}");
|
|
if (OnProgress != null) OnProgress(readByted, totalBytes, percent);
|
|
if (percent == 1) isComplete = true;
|
|
}
|
|
if (!isComplete)
|
|
{
|
|
await UniTask.Delay(TimeSpan.FromMilliseconds(100));//0.1초에 한번씩
|
|
CountPercent7ZipAsync(totalBytes).Forget();
|
|
}
|
|
}
|
|
}
|
|
}
|