Post List

2016년 4월 6일 수요일

C# 애서 ZIP 파일 압축 및 해제와 unix 압축파일 (Z, tar, tgz) 압축 해제하기

FileCompression.NET

사용하는 곳이 많지는 않겠지만, 예전에 작업해 놓은게 있어서 소스를 정리해서 올립니다.

Unix에서 사용하는 압축형식 중 Z, Tgz, Tar의 압축을 해제하는 소스코드 입니다.

evenZip, SharpZipLib, 7zip 의 3가지 외부 라이브러리를 사용했으며,
7zip의 경우 C#용 dll이 아니므로 해당 dll을 사용하는 LzwInputStream.cs를  포함시켰습니다.

  • Z file : 7z.dll and LzwInputStream.cs
  • Zip file : SharpZipLib
  • Tgz file : SevenZip
  • Tar file : SharpZipLib

* 2016-04-08 수정
  - UnTgz를 Thread Safe한 코드로 수정하였습니다. 방법을 알려주신 Alex Lee ( https://www.facebook.com/csharpstudy )님께 감사드립니다.

* 2016-04-11 수정
  - UnTgz의 Event가 재사용 가능성이 희박한 코드라서 Lambda식으로 수정하였습니다. Pull Request 주신 Bongho Lee님 (https://github.com/techcap) 님께 감사드립니다.

전체 소스 및 Project 설정 사항은 아래 Link를 참고해 주세요.


* 소스코드


using ICSharpCode.SharpZipLib.Tar;
using ICSharpCode.SharpZipLib.Zip;
using SevenZip;
using System;
using System.Collections.Generic;
using System.IO;

// 솔루션 빌드 후 이벤트에 아래 줄 추가 (단 최종경로 확인 필수 : Project명에 . 들어가는 경우 실제 폴더 구조랑 다를 수 있음
// COPY /Y "$(SolutionDir)$(ProjectName)\7z.dll" "$(SolutionDir)$(SolutionName)\$(OutDir)\7z.dll"

namespace LunaStar.Util
{
    public class FileCompression
    {
        /// <summary>
        /// Unix에서 생성한 Z 파일의 압축해제
        /// </summary>
        /// <param name="zFileName">압축해제할 Z 파일이름</param>
        /// <returns>압축 해제 성공 여부</returns>
        static public bool UnUnixZ(string zFileName)
        {
            try
            {
                FileInfo fi = new FileInfo(zFileName);
                string directoryName = fi.DirectoryName;
                string fileName = Path.GetFileNameWithoutExtension(zFileName);
                string outputFileName = $"{directoryName}\\{fileName}";

                using (Stream inStream = new LzwInputStream(File.OpenRead(zFileName)))
                using (FileStream outStream = File.Create(outputFileName))
                {
                    int bytesRead;
                    byte[] buffer = new byte[4096];

                    while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        outStream.Write(buffer, 0, bytesRead);
                    }
                }
            }
            catch (Exception e)
            {
                throw new AggregateException("Fail FileCompression.UnUnixZ", e);
            }
            return true;
        }

        /// <summary>
        /// 특정 폴더를 ZIP으로 압축
        /// </summary>
        /// <param name="targetFolderPath">압축 대상 폴더 경로</param>
        /// <param name="zipFilePath">저장할 ZIP 파일 경로</param>
        /// <param name="password">압축 암호</param>
        /// <param name="isDeleteFolder">폴더 삭제 여부</param>
        /// <returns>압축 성공 여부</returns>
        public static bool Zip(string targetFolderPath, string zipFilePath, string password, bool isDeleteFolder)
        {
            if (!Directory.Exists(targetFolderPath)) // 폴더가 존재하는 경우에만 수행
                return false;

            List<string> fileList = GenerateFileList(targetFolderPath); // 압축 대상 폴더의 파일 목록

            int pathLength = (Directory.GetParent(targetFolderPath)).ToString().Length + 1; // find number of chars to remove. from orginal file path. remove '\'

            using (ZipOutputStream zipOutputStream = new ZipOutputStream(File.Create(zipFilePath))) // ZIP 스트림 생성.
            {
                if (password != null && password != string.Empty) // 패스워드가 있는 경우 패스워드 지정
                    zipOutputStream.Password = password;

                zipOutputStream.SetLevel(9); // 암호화 레벨.(최대 압축)

                ZipEntry zipEntry;
                foreach (string fileName in fileList)
                {
                    zipEntry = new ZipEntry(fileName.Remove(0, pathLength));
                    zipOutputStream.PutNextEntry(zipEntry);

                    try
                    {
                        if (!fileName.EndsWith(@"/")) // 파일인 경우
                        {
                            using (FileStream fileStream = File.OpenRead(fileName))
                            {
                                byte[] buffer = new byte[fileStream.Length];
                                fileStream.Read(buffer, 0, buffer.Length);
                                zipOutputStream.Write(buffer, 0, buffer.Length);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        // 오류가 난 경우 생성 했던 파일을 삭제.
                        if (File.Exists(zipFilePath))
                            File.Delete(zipFilePath);

                        throw new AggregateException("FileCompression.Zip : Fail to ZipOutputStream", e);
                    }
                    finally
                    {
                        zipOutputStream.Finish(); // 압축 종료
                        zipOutputStream.Close();
                    }
                }
            }

            if (isDeleteFolder) // 폴더 삭제를 원할 경우 폴더 삭제
            {
                try
                {
                    Directory.Delete(targetFolderPath, true);
                }
                catch (Exception e)
                {
                    throw new AggregateException("FileCompression.Zip : Fail to delete folder", e);
                }
            }

            return true;
        }

        /// <summary>
        /// 파일, 폴더 목록 생성
        /// </summary>
        /// <param name="directory">폴더 경로</param>
        /// <returns>폴더, 파일 목록(ArrayList)</returns>
        private static List<string> GenerateFileList(string directory)
        {
            var fileList = new List<string>();

            bool isEmpty = true;

            try
            {
                foreach (string fileName in Directory.GetFiles(directory)) // 폴더 내의 파일 추가
                {
                    fileList.Add(fileName);
                    isEmpty = false;
                }
            }
            catch (Exception e)
            {
                throw new AggregateException("FileCompression.GenetateFileList : Fail to add files", e);
            }

            if (isEmpty)
            {
                try
                {
                    if (Directory.GetDirectories(directory).Length == 0) // 파일이 없고, 폴더도 없는 경우 자신의 폴더 추가
                        fileList.Add(directory + @"/");
                }
                catch (Exception e)
                {
                    throw new AggregateException("FileCompression.GenetateFileList : Fail to add self", e);
                }
            }

            try
            {
                foreach (string directoryName in Directory.GetDirectories(directory)) // 폴더 내 폴더 목록
                {
                    foreach (string fileName in GenerateFileList(directoryName)) // 해당 폴더로 다시 GenerateFileList 재귀 호출
                    {
                        fileList.Add(fileName); // 해당 폴더 내의 파일, 폴더 추가
                    }
                }
            }
            catch (Exception e)
            {
                throw new AggregateException("FileCompression.GenetateFileList : Fail to add directories", e);
            }

            return fileList;
        }

        /// <summary>
        /// ZIP 압축 파일 풀기
        /// </summary>
        /// <param name="zipFilePath">ZIP파일 경로</param>
        /// <param name="unZipTargetFolderPath">압축 풀 폴더 경로</param>
        /// <param name="password">해지 암호</param>
        /// <param name="isDeleteZipFile">zip파일 삭제 여부</param>
        /// <returns>압축 풀기 성공 여부 </returns>
        public static bool Unzip(string zipFilePath, string unZipTargetFolderPath, string password, bool isDeleteZipFile)
        {
            if (!File.Exists(zipFilePath)) // ZIP 파일이 있는 경우만 수행
                return false;

            using (ZipInputStream zipInputStream = new ZipInputStream(File.OpenRead(zipFilePath))) // ZIP 스트림 생성
            {
                if (password != null && password != string.Empty) // 패스워드가 있는 경우 패스워드 지정
                    zipInputStream.Password = password;

                try
                {
                    ZipEntry theEntry;

                    while ((theEntry = zipInputStream.GetNextEntry()) != null) // 반복하며 파일을 가져옴
                    {
                        string directoryName = Path.GetDirectoryName(theEntry.Name); // 폴더 명칭
                        string fileName = Path.GetFileName(theEntry.Name); // 파일 명칭

                        Directory.CreateDirectory(unZipTargetFolderPath + directoryName); // 폴더 생성

                        if (fileName == string.Empty) // 파일 이름이 없으면 Pass
                            continue;

                        using (FileStream streamWriter = File.Create((unZipTargetFolderPath + theEntry.Name))) // 파일 스트림 생성.(파일생성)
                        {
                            byte[] data = new byte[2048];

                            while (true) // 파일 복사
                            {
                                int size = zipInputStream.Read(data, 0, data.Length);

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

                            streamWriter.Close(); // 파일스트림 종료
                        }
                    }
                }
                catch (Exception e)
                {
                    throw new AggregateException("FileCompression.Unzip : Fail to ZipInputStream", e);
                }
                finally
                {
                    zipInputStream.Close(); // ZIP 파일 스트림 종료
                }
            }

            if (isDeleteZipFile) // ZIP파일 삭제를 원할 경우 파일 삭제
            {
                try
                {
                    File.Delete(zipFilePath);
                }
                catch (Exception e)
                {
                    throw new AggregateException("FileCompression.Unzip : Fail to delete zip file", e);
                }
            }

            return true;
        }

        /// <summary>
        /// tgz 파일 압축 풀기
        /// </summary>
        /// <param name="fileName">tgz 파일명</param>
        /// <param name="savePath">저장할 파일 위치</param>
        /// <returns>압축 풀기 성공 여부 </returns>
        public static bool UnTgz(string fileName, string savePath)
        {
            if (!File.Exists(fileName))
                return false;

            using (SevenZipExtractor sevenZipExtractor = new SevenZipExtractor(fileName))
            {
                try
                {
                    sevenZipExtractor.EventSynchronization = EventSynchronizationStrategy.AlwaysSynchronous;
                    savePath = savePath.Replace(@"\\", @"\");
                    sevenZipExtractor.ExtractionFinished +=
                        (sender, e) =>
                        {
                            try
                            {
                                List<string> fileList = GenerateFileList(savePath);
                                foreach (string file in fileList)
                                {
                                    var fi = new FileInfo(file);
                                    if (fi.Extension.ToUpper() == ".TAR")
                                    {
                                        UnTar(file);
                                        fi.Delete();
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                throw new AggregateException("FileCompression.ExtractionFinishedHandler : Fail sevenZipExtractor", ex);
                            }
                        };

                    sevenZipExtractor.ExtractArchive(savePath);
                }
                catch (Exception e)
                {
                    throw new AggregateException("FileCompression.UnTgz : Fail sevenZipExtractor", e);
                }
            }

            return true;
        }

        /// <summary>
        /// tar 파일 압축 해제
        /// </summary>
        /// <param name="TarName">tar 파일명</param>
        public static void UnTar(string TarName)
        {
            try
            {
                FileInfo fi = new FileInfo(TarName);

                using (TarInputStream s = new TarInputStream(File.OpenRead(TarName)))
                {
                    TarEntry theEntry;

                    while ((theEntry = s.GetNextEntry()) != null)
                    {
                        string FullName = string.Format("{0}\\{1}", fi.DirectoryName, theEntry.Name);
                        string DirName = Path.GetDirectoryName(FullName);
                        string FileName = Path.GetFileName(FullName);

                        if (!Directory.Exists(DirName)) Directory.CreateDirectory(DirName);

                        if (FileName != string.Empty)
                        {
                            FileStream SW = File.Create(FullName);

                            int Size = 2048;
                            byte[] data = new byte[2048];
                            while (true)
                            {
                                Size = s.Read(data, 0, data.Length);
                                if (Size > 0) SW.Write(data, 0, Size);
                                else break;
                            }
                            SW.Close();
                        }
                    }
                }

            }
            catch (Exception e)
            {
                throw new AggregateException("FileCompression.UnTar : Fail untar", e);
            }
        }
    }
}

댓글 없음:

댓글 쓰기