Files

194 lines
7.1 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
namespace CommonLibrary
{
internal class ProgressStream : Stream
{
private readonly Stream insideStream;
private readonly IProgress<int> insideReadProgress;
private readonly IProgress<int> insideWriteProgress;
public ProgressStream(Stream stream, IProgress<int> readProgress, IProgress<int> writeProgress)
{
insideStream = stream;
insideReadProgress = readProgress;
insideWriteProgress = writeProgress;
}
public override bool CanRead => insideStream.CanRead;
public override bool CanSeek => insideStream.CanSeek;
public override bool CanWrite => insideStream.CanWrite;
public override long Length => insideStream.Length;
public override long Position
{
get => insideStream.Position;
set => insideStream.Position = value;
}
public override void Flush() => insideStream.Flush();
public override long Seek(long offset, SeekOrigin origin) => insideStream.Seek(offset, origin);
public override void SetLength(long value) => insideStream.SetLength(value);
public override int Read(byte[] buffer, int offset, int count)
{
int bytesRead = insideStream.Read(buffer, offset, count);
insideReadProgress?.Report(bytesRead);
return bytesRead;
}
public override void Write(byte[] buffer, int offset, int count)
{
insideStream.Write(buffer, offset, count);
insideWriteProgress?.Report(count);
}
}
public class ProgressPacker
{
public void Pack(string sourceDirectoryName, string packFileName, IProgress<double> progress)
{
sourceDirectoryName = Path.GetFullPath(sourceDirectoryName);
FileInfo[] sourceFiles = new DirectoryInfo(sourceDirectoryName).GetFiles("*", SearchOption.AllDirectories);
double totalBytes = sourceFiles.Sum(f => f.Length);
long currentBytes = 0;
using (ZipArchive zipArchive = ZipFile.Open(packFileName, ZipArchiveMode.Create))
{
foreach (FileInfo file in sourceFiles)
{
// 경로 구분자를 '/' 로 통일 (Windows 탐색기 호환)
string entryName = file.FullName.Substring(sourceDirectoryName.Length + 1)
.Replace(Path.DirectorySeparatorChar, '/');
ZipArchiveEntry entry = zipArchive.CreateEntry(entryName);
entry.LastWriteTime = file.LastWriteTime;
using (Stream inputStream = file.OpenRead())
using (Stream outputStream = entry.Open())
using (Stream progressStream = new ProgressStream(inputStream,
new BasicProgress<int>(i =>
{
currentBytes += i;
progress?.Report(currentBytes / totalBytes);
}), null))
{
// ✅ 방향 수정: inputStream → progressStream → outputStream
progressStream.CopyTo(outputStream);
}
}
}
}
public void UnPack(string packFileName, string destinationDirectoryName, IProgress<double> progress)
{
using (ZipArchive zipArchive = ZipFile.OpenRead(packFileName))
{
double totalBytes = zipArchive.Entries.Sum(f => f.Length);
long currentBytes = 0;
foreach (ZipArchiveEntry entry in zipArchive.Entries)
{
// 디렉터리 엔트리 건너뜀
if (string.IsNullOrEmpty(entry.Name))
continue;
string fileName = Path.Combine(destinationDirectoryName, entry.FullName);
Directory.CreateDirectory(Path.GetDirectoryName(fileName));
using (Stream inputStream = entry.Open())
using (Stream outputStream = File.OpenWrite(fileName))
using (Stream progressStream = new ProgressStream(outputStream, null,
new BasicProgress<int>(i =>
{
currentBytes += i;
progress?.Report(currentBytes / totalBytes);
})))
{
inputStream.CopyTo(progressStream);
}
File.SetLastWriteTime(fileName, entry.LastWriteTime.LocalDateTime);
}
}
}
public List<PackList> GetTopLevelItem(string packFileName)
{
List<PackList> packLists = new List<PackList>();
using (ZipArchive archive = ZipFile.OpenRead(packFileName))
{
// 최상위 항목들(폴더/파일명) 추출
var results = new List<PackList>();
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var entry in archive.Entries)
{
string path = entry.FullName.Replace('\\', '/');
if (string.IsNullOrEmpty(path)) continue;
int firstSlash = path.IndexOf('/');
if (firstSlash < 0)
{
// ZIP 루트에 바로 들어있는 항목
if (string.IsNullOrEmpty(entry.Name))
{
// 디렉터리 엔트리
if (seen.Add(path.TrimEnd('/')))
results.Add(new PackList { IsDirectory = true, Name = path.TrimEnd('/') });
}
else
{
// 파일
if (seen.Add(path))
results.Add(new PackList { IsDirectory = false, Name = path });
}
}
else
{
// 하위 경로가 있음 → 최상위는 폴더
string top = path.Substring(0, firstSlash);
if (!string.IsNullOrEmpty(top) && seen.Add(top))
{
results.Add(new PackList { IsDirectory = true, Name = top });
}
}
}
return results;
}
}
public struct PackList
{
public bool IsDirectory { get; set; }
public string Name { get; set; }
}
}
public class BasicProgress<T> : IProgress<T>
{
private readonly Action<T> actionHandler;
public BasicProgress(Action<T> handler)
{
actionHandler = handler;
}
public void Report(T value)
{
actionHandler(value);
}
}
}