NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    C# C#을 이용한 압축 풀기와 압축 하기. (C#, Zip, Gzip, Tar, Bzip2)

    페이지 정보

    본문

    안녕하세요. 소심비형입니다. 오늘은 C#을 이용하여 압축에 관련된 모듈을 작성해 보도록 하겠습니다. 이 부록에서 다루고 있는 내용은 단순히 압축하고 해제하는 방법만 정리합니다. 그리고, 압축 알고리즘을 제공하는 오픈 소스인 SharpZipLib 라이브러리를 이용하게 됩니다. SharpZipLib는 GPL을 따르고 있으므로 주의가 필요합니다. 만약, 라이센스에서 자유롭게 압축 관련 개발을 해야 한다면, DotNetZip 라이브러리를 사용하세요. DotNetZip은 MS-PL(Microsoft Public License)이므로 사용상 제약이 없습니다.

    [ 다운로드: SharpZipLib ]

    [ 다운로드: DotNetZip ]

     

    이 글에서는 SharpZipLib을 이용하여 구현합니다. DotNetZip은 다음에 시간될 때 한번 알아보도록 할께요. 언제가 될지는 모르겠지만...

    img.gif

     

     

    우선 SharpZipLib을 다운로드 받은 후 원하는 위치에 압축을 풀어줍니다. 안에 있는 ICSharpCode.SharpZLib.sln 솔루편 파일을 실행하세요. 비주얼 스튜디오가 열리면, Ctrl + Shift + B를 눌러 솔루션 빌드를 해야 합니다. 이 후 Debug 또는 Release폴더로 이동하여 ICSharpCode.SharpZipLib.dll 파일을 자신의 프로젝트로 복사해옵니다. 만약, 해당 위치에 파일이 없다면 상위 폴더로 이동하여 bin폴더안에 있는지도 확인 해보세요.

     

    빌드가 귀찮으면 이 글에 첨부되어 있는 파일을 다운로드 받아서 사용해도 됩니다.

     

    저의 경우 아래와 같이 ExtLib에 복사하여 넣어 두었습니다.

    img.png

     

     

    이제 압축 관련 작업을 진행할 뷰를 하나 추가한 후 메뉴를 연결해줍니다. SCSF의 Guidance를 이용하여 Client.Launcher에 View를 추가하세요. View의 이름은 ZipView로 설정합니다. 이 외에 추가로 설정할 내용은 없으므로 "마침"을 클릭하세요.

    아래 그림처럼 런처에 ZipView가 추가되었습니다.

    img.png

     

     

    메뉴에 추가한 뷰를 연결해야 합니다. Client.Launcher의 ModuleController에 아래와 같이 코드를 추가합니다.

    ModuleController.cs

    private void ExtendMenu()
    {
        //TODO: add menu items here, normally by calling the "Add" method on
        // on the WorkItem.UIExtensionSites collection.
        //
        NG.Management.Menu.Manager menuManager = new Manager(WorkItem);
        RibbonPages pages = menuManager.GetMenuFactory<RibbonPages>();
        Items items = pages.AddNew("Launcher Page").Groups.AddNew("Launcher Group").Items;
        items.AddNew<BarButtonItem>("Dock Open").AddCommand("DockOpen");
        items.AddNew<BarButtonItem>("Tab Open").AddCommand("TabOpen");
        items.AddNew<BarButtonItem>("Linq Open").AddCommand("LinqOpen");
        items.AddNew<BarButtonItem>("Box Plot Open").AddCommand("BoxPlotOpen");
        items.AddNew<BarButtonItem>("FTP Client").AddCommand("FtpClientOpen");
        items.AddNew<BarButtonItem>("Excel Open").AddCommand("ExcelOpen");
        items = pages["Launcher Page"].Groups.AddNew("Utilies").Items;
        items.AddNew<BarButtonItem>("NGMASTER Zip").AddCommand("ZipOpen");
        menuManager.WorkItem = WorkItem.RootWorkItem;
        pages = menuManager.GetMenuFactory<RibbonPages>();
        pages.IndexChange(100, pages[0]);
    }

     

     

    메뉴를 추가했으니, 해당 메뉴를 클릭할 때 ZipView를 오픈 해줄 커멘드 핸들러도 추가합니다.

    ModuleController.cs

    [CommandHandler("ZipOpen")]
    public void OnNGZipOpenHandler(object sender, EventArgs e)
    {
        NG.Management.View.Manager viewManager = new Management.View.Manager(WorkItem);
        viewManager.TabView.Show<ZipView>("NGZip");
    }

     

     

    본격적으로 개발하기 위한 준비가 완료 되었습니다. 이제 추가한 ZipView를 디자인 합니다. 단순하게 압축하고 해제하기 위한 UI이기 때문에 복잡하게 만들지 않아도 될거 같네요. 만약에 연결 프로그램 방식으로 처리하려면 레지스트리 작업을 해줘야 합니다.

    img.png

     

     

    디자인이 완성 되었으면 각 버튼에 이벤트 핸들러를 연결해줍니다. 완료된 코드는 아래와 같습니다.

    ZipView.cs

    //----------------------------------------------------------------------------------------
    // patterns & practices - Smart Client Software Factory - Guidance Package
    //
    // This file was generated by the "Add View" recipe.
    //
    // This class is the concrete implementation of a View in the Model-View-Presenter
    // pattern. Communication between the Presenter and this class is acheived through
    // an interface to facilitate separation and testability.
    // Note that the Presenter generated by the same recipe, will automatically be created
    // by CAB through [CreateNew] and bidirectional references will be added.
    //
    //
    //
    //
    // Latest version of this Guidance Package: http://go.microsoft.com/fwlink/?LinkId=62182
    //----------------------------------------------------------------------------------------
    using System;
    using System.Windows.Forms;
    using Microsoft.Practices.CompositeUI.SmartParts;
    using Microsoft.Practices.ObjectBuilder;
    using NG.Infrastructure.Interface;
    
    namespace NG.Client.Launcher
    {
        public partial class ZipView : UserControl, IZipView
        {
            public ZipView() { InitializeComponent(); }
            protected override void OnLoad(EventArgs e) { _presenter.OnViewReady(); base.OnLoad(e); }
            private void btnOpen_Click(object sender, EventArgs e) { }
            private void btnSaveDirectory_Click(object sender, EventArgs e) { }
            private void btnCompressing_Click(object sender, EventArgs e) { }
            private void btnExtracting_Click(object sender, EventArgs e) { }
        }
    }

     

     

    각 기능을 개발하기 전에 가장 앞에 있는 압축 포멧을 설정할 수 있도록 항목을 추가해야겠죠? 압축 포멧을 선택하는 콤보 박스(드롭 다운 리스트)를 초기화 합니다. 아래와 같이 View의 OnLoad 이벤트에서 컨트롤을 초기화 합니다.

    ZipView.cs

    protected override void OnLoad(EventArgs e)
    {
        _presenter.OnViewReady();
        base.OnLoad(e);
        this.comboBoxEdit1.Properties.Items.Add("Zip");
        this.comboBoxEdit1.Properties.Items.Add("Gzip");
        this.comboBoxEdit1.Properties.Items.Add("Bzip2");
        this.comboBoxEdit1.Properties.Items.Add("Tar");
        this.comboBoxEdit1.SelectedIndex = 0;
    }

     

     

    위 코드의 하이라이트 되어 있는 코드는 아래와 같이 리팩토링 할 수 있습니다. 38~42라인을 블럭으로 선택하고, Alt + Shift + F10(스마트 태그)을 눌러서 "메서드 추출"을 선택하세요.

    ZipView.cs

    protected override void OnLoad(EventArgs e)
    {
        _presenter.OnViewReady();
        base.OnLoad(e);
        InitCompressingFormat();
    }
    
    private void InitCompressingFormat()
    {
        comboBoxEdit1.Properties.Items.Add("Zip");
        comboBoxEdit1.Properties.Items.Add("Gzip");
        comboBoxEdit1.Properties.Items.Add("Bzip2");
        comboBoxEdit1.Properties.Items.Add("Tar");
        comboBoxEdit1.SelectedIndex = 0;
    }

     

     

    이렇게하면, 코드의 재사용성이 증가하게 됩니다. 이제 압축 포멧을 지정하는 컨트롤을 초기화 하려면 InitCompressionFormat 메서드만 호출하면 되기 때문입니다. 여기에서 좀 더 쓸만하게 변경하려면 SelectedIndex와 항목의 아이템들을 파라메터로 받으면 됩니다. 이런 일련의 작업을 리팩토링이라고 부릅니다.

    img.jpg

     

     

    이제 압축하거나 해제할 파일을 불러오는 이벤트를 처리합니다.

    ZipView.cs

    private void btnOpen_Click(object sender, EventArgs e)
    {
        OpenFileDialog dialog = new OpenFileDialog();
        dialog.Multiselect = true;
    
        if (dialog.ShowDialog() == DialogResult.OK)
            gridControl1.DataSource = dialog.FileNames;
    }

     

     

    정상적으로 파일을 가져오고 있네요. 그리드 컨트롤에 데이타도 바인딩 되었습니다.

    img.png

     

     

    압축하거나 해제할 때 파일이 생성되는 위치를 지정하는 이벤트를 처리합니다.

    ZipView.cs

    private void btnSaveDirectory_Click(object sender, EventArgs e)
    {
        FolderBrowserDialog dialog = new FolderBrowserDialog();
    
        if (dialog.ShowDialog() == DialogResult.OK)
            textEdit1.Text = dialog.SelectedPath;
    }

     

     

    위 코드는 그리 어려운 내용이 없으므로, 테스트 해볼 필요까지는 없을거 같네요. 다음으로, "압축 하기" 버튼의 이벤트를 처리합니다.

    ZipView.cs

    private void btnCompressing_Click(object sender, EventArgs e)
    {
        try
        {
            using (ZipOutputStream stream = new ZipOutputStream(File.Create(string.Format("{0}.{1}", Path.Combine(textEdit1.Text, DateTime.Now.ToString("yyyyMMddHHmmss")), comboBoxEdit1.Text))))
            {
                // 압축율 최대 지정 (0~9)
                stream.SetLevel(9);
                //stream.Password = "NGMASTER";
                byte[] buffer = new byte[1024 * 10];
                string[] fileNames = gridControl1.DataSource as string[];
    
                foreach (string fileName in fileNames)
                {
                    ZipEntry entry = new ZipEntry(Path.GetFileName(fileName));
                    entry.DateTime = DateTime.Now;
                    stream.PutNextEntry(entry);
    
                    using (FileStream fs = File.OpenRead(fileName))
                    {
                        int sourceBytes;
    
                        do
                        {
                            sourceBytes = fs.Read(buffer, 0, buffer.Length);
                            stream.Write(buffer, 0, sourceBytes);
                        } while (sourceBytes > 0);
                    }
                }
    
                stream.Finish();
                stream.Close();
    
                NG.Controls.MessageBox.Show("작업이 완료되었습니다.", NG.Controls.MessageType.Information);
            }
        }
        catch (Exception ex)
        {
            Logger.Error(this.GetType(), ex);
            NG.Controls.MessageBox.Show("작업이 실패하였습니다.", NG.Controls.MessageType.Error);
        }
    }

     

     

    정상적으로 동작하는지 파일을 압축 해보도록 하겠습니다. 우선 적당한 파일 몇개를 선택하고, 압축 포멧은 Zip을 선택합니다.

    img.png

     

     

    "압축 하기"버튼을 클릭하면, "작업이 완료되었습니다." 메시지가 표시됩니다. 물론, 어떤 이유(버그?)로 인해 작업이 실패한다면 메시지도 다르게 보이겠죠?

    img.png

     

     

    압축한 파일을 열어보면, 대략 어느정도 압축되었는지 확인할 수 있습니다. 이미지의 경우 어느정도 압축된 포멧을 사용하기 때문에 압축율이 그리 높지 않습니다.

    img.png

     

     

    이제 압축된 파일을 해제해보고, 원본 파일과 동일한지 비교해보면 될거 같네요. 먼저, "압축 해제"버튼의 이벤트를 처리해야합니다.

    ZipView.cs

    private void btnExtracting_Click(object sender, EventArgs e)
    {
        ZipFile zf = null;
    
        try
        {
            string[] fileNames = gridControl1.DataSource as string[];
    
            foreach (string fileName in fileNames)
            {
                FileStream fs = File.OpenRead(fileName);
                zf = new ZipFile(fs);
                //zf.Password = "NGMASTER";
    
                string folderName = Path.Combine(textEdit1.Text, Path.GetFileName(fileName).Split('.')[0]);
    
                foreach (ZipEntry zipEntry in zf)
                {
                    if (!zipEntry.IsFile)
                        continue;
    
                    string entryFileName = zipEntry.Name;
                    byte[] buffer = new byte[4096];
                    Stream zipStream = zf.GetInputStream(zipEntry);
                    string fullZipToPath = Path.Combine(folderName, entryFileName);
                    string directoryName = Path.GetDirectoryName(fullZipToPath);
    
                    if (directoryName.Length > 0)
                        Directory.CreateDirectory(directoryName);
    
                    using (FileStream streamWriter = File.Create(fullZipToPath))
                        StreamUtils.Copy(zipStream, streamWriter, buffer);
                }
            }
    
            NG.Controls.MessageBox.Show("작업이 완료되었습니다.", NG.Controls.MessageType.Information);
        }
        catch (Exception ex)
        {
            Logger.Error(this.GetType(), ex);
            NG.Controls.MessageBox.Show("작업이 실패하였습니다.", NG.Controls.MessageType.Error);
        }
        finally
        {
            if (zf != null)
            {
                zf.IsStreamOwner = true;
                zf.Close();
            }
        }
    }

     

     

    압축 해제도 잘 되는군요. 이 외에도 상당히 많은 기능들을 구현해 놓았습니다. 다른 압축 포멧을 이용하여 처리하는 부분은 직접 해보시기 바랍니다. 사용되는 클래스만 다를뿐 방식은 동일하기 때문에 쉽게 해결하실 수 있을거라 생각되는군요^^;

     

    FTP서버에서 압축하거나 풀수도 있고, HTTP를 이용하여 웹 서버에서 다운로드 할 때 파일을 압축해서 내려보낼수도 있습니다. 이 라이브러리를 사용하면 아주 쉽게 파일을 압축하고 해제할 수 있지만, GPL 라이센스이므로 소스를 공개할 의무가 있습니다. 따라서, 상업적인 용도로 사용하기에는 깊은(?) 고민이 필요할지도 모릅니다.

    img.gif

     

     

    정확하게 라이센스 정책을 읽어보지는 않았는데요. 전체 소스 공개인지... 라이브러리를 사용한 모듈에 한정된 것인지는 한번 확인해 보시고 사용하는게 좋을거 같네요.

    다음 시간에...

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.