Blog Dla Młodszych Programistów C#/.NET

W dzisiejszym artykule stworzymy małą aplikację, która będzie nam kompresować pliki z wybranego folderu do pliku zip. Pokaże Ci również jak te pliki później przywrócić, czyli rozpakować do formatu wyjściowego. Oprócz tego zaimplementujemy kilka dodatkowych funkcji, dzięki czemu nasza aplikacja będzie bardziej uniwersalna i przydatna do codziennego użytku. Całość stworzymy w języku C#.

Praca z Plikami ZIP w C#/.NET w Praktyce

Projekt


Jeżeli chcesz programować równocześnie ze mną, to załóż sobie nowy projekt Windows Forms, podobnie jak to robiliśmy w poprzednich artykułach. Jeżeli nie wiesz jak to zrobić, to wróć proszę do tych materiałów. Wybór frameworka nie ma tutaj znaczenia, tak samo możesz zrobić taką aplikację w WPF, ASP.NET Core, Xamarinie, Blazor czy jako prosta aplikacja konsolowa. Wszędzie ten rdzeń, główny kod, który będziemy tworzyć będzie wyglądał identycznie. Ja dla prostoty przykładu wybrałem Windows Forms.

Praca z Plikami ZIP w C#/.NET w Praktyce 1


Założenia projektu


Przed implementacja warto na chwilą usiąść i zastanowić się co dokładnie chcemy zrobić. Opiszę Ci teraz wszystkie funkcje, które chcemy zaimplementować w naszej aplikacji.

1) Podział aplikacji na 2 sekcje. W pierwszej będziemy mieli pakowanie plików do ZIP (kompresja), a w drugiej rozpakowanie pliku ZIP do formatu źródłowego (wyodrębnienie).

Zacznijmy od kompresji plików.

2) Wybór folderu, z którego będziemy kompresować wszystkie pliki.
3) Wyświetlenie wybranej ścieżki na formatce głównej.
4) Kompresja wszystkich plików z wybranego wcześniej folderu.
5) Skopiowanie skompresowanego pliku do schowka, w celu szybszego dostępu do niego.

Następnie mamy rozpakowywanie plików.

6) Wybór pliku o rozszerzeniu zip, który chcemy rozpakować.
7) Wyświetlenie na ekranie ścieżki do wybranego pliku.
8) Rozpakowanie pliku z wybranej wcześniej ścieżki.
9) Wyświetlenie wcześniej rozpakowanych plików w eksploratorze plików.

Oprócz tego warto dodać jakąś walidację i podstawową obsługę błędów.

Przejdźmy do stworzenia formularza głównego naszej aplikacji. Zacznijmy od widoku głównego.


User interface


Nie będziemy się tutaj aż tak bardzo skupiać na wyglądzie naszej aplikacji. Skorzystamy z podstawowych kontrolek i domyślnego wyglądu. Przede wszystkim skupiamy się w tym materiale na samej logice i funkcjonalności.

Dodaj proszę na formatkę główną aplikacji kilka kontrolek:

2 GroupBoxy w celu oddzielenia wizualnego logiki pakowania plików od rozpakowywania plików.

Na każdym z nich umieść po 3 przyciski (Button) oraz po 1 etykiecie (Label).

Pakowanie plików (ZIP):
Button (Name: btnChooseFolder, Text: Wybierz folder…).
Label (Name: lbZipPath, Text: [pusty]).
Button (Name: btnZipFile, Text: Skompresuj pliki).
Button (Name: btnCopyZipFile, Text: Kopiuj skompresowany plik).

Rozpakowywanie plików (UNZIP):
Button (Name: btnChooseZipFile, Text: Wybierz plik .zip...).
Label (Name: btnUnzipFile, Text: [pusty]).
Button (Name: btnUnzipFile, Text: Rozpakuj pliki).
Button (Name: btnShowUnzipFiles, Text: Pokaż rozpakowane pliki).

Możemy dla obu sekcji nadać 2 rożne kolory tła, dzięki temu zostanie to jeszcze lepiej odseperowane.

Super, tak to wygląda wizualnie:

Praca z Plikami ZIP w C#/.NET w Praktyce - 2

I możemy przejść do implementacji całej logiki.


Logika aplikacji


Kliknij teraz 2 krotnie na każdy przycisk w celu utworzenia metod powiązanych ze zdarzeniami Click dla każdego przycisku.
private void btnChooseFolder_Click(object sender, EventArgs e)
{
}

private void btnZipFile_Click(object sender, EventArgs e)
{
}

private void btnCopyZipFile_Click(object sender, EventArgs e)
{
}

private void btnChooseZipFile_Click(object sender, EventArgs e)
{
}

private void btnUnzipFile_Click(object sender, EventArgs e)
{
}

private void btnShowUnzipFiles_Click(object sender, EventArgs e)
{
}

I teraz po kolei zaimplementujemy te wszystkie metody.


Pakowanie plików do ZIP (kompresja)


Najpierw zaimplementujemy sobie logikę wyboru folderu z którego wszystkie pliki zostaną skompresowane.
private void btnChooseFolder_Click(object sender, EventArgs e)
{
    using var folderBrowserDialog = new FolderBrowserDialog();

    var result = folderBrowserDialog.ShowDialog();

    if (result == DialogResult.OK)
    {
        lbZipPath.Text = folderBrowserDialog.SelectedPath;
    }
}

Skorzystamy tutaj z klasy FolderBrowserDialog, dzięki której użytkownik w łatwy sposób będzie mógł wybrać folder z którego pliki zostaną później skompresowane. Jeżeli folder zostanie wybrany, to wyświetlimy do niego ścieżkę w etykiecie lbZipPath.

Jeżeli ścieżka została wybrana, to możemy skompresować wszystkie pliki wewnątrz tego folderu.
private readonly string _filePath = Path.Combine(Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory), "Files");

public Form1()
{
    InitializeComponent();

    if (!Directory.Exists(_filePath))
        Directory.CreateDirectory(_filePath);
}

private void btnZipFile_Click(object sender, EventArgs e)
{
    if (string.IsNullOrWhiteSpace(lbZipPath.Text))
    {
        MessageBox.Show("Wybierz proszę najpierw folder, który chcesz skompresować");
        return;
    }

    var compressedFileName = $"{Path.GetFileName(lbZipPath.Text)}.zip";

    var destinationFileName = Path.Combine(_filePath, compressedFileName);		

    ZipFile.CreateFromDirectory(lbZipPath.Text, destinationFileName);

    MessageBox.Show("Folder został skompresowany!");
}

Najpierw upewnimy się, czy na pewno folder został już wcześniej wybrany. Jeżeli nie został wybrany, to wyświetlimy odpowiedni komunikat i nie przechodzimy do dalszej części tej metody. Jeżeli mamy wybrany folder, to najpierw przygotujemy sobie ścieżkę i nazwę pliku docelową, czyli taką pod którą zostanie utworzony plik skompresowany. Możemy taki plik sobie utworzyć w folderze Files w folderze aplikacji bin. Warto też w konstruktorze upewnić się, że istnieje folder Files w plikach aplikacji. Jeżeli takiego pliku nie ma, to musimy go utworzyć, tak aby nie został rzucony błąd podczas próby zapisu. W celu skompresowania plików w C# skorzystamy z klasy ZipFile i jej statycznej metody CreateFromDirectory, do której jako parametry przekażemy folder, który chcemy kompresować i właśnie docelową nazwę pliku zip. Na koniec wyświetlimy komunikat informujący użytkownika, że cały proces przeszedł pozytywnie.

Pozostaje nam jeszcze implementacja metody kopiowania nowo utworzonego pliku, dzięki czemu użytkownik nie będzie musiał szukać na dysku nowego pliku, tylko od razu będzie go miał w schowku.
private void btnCopyZipFile_Click(object sender, EventArgs e)
{
    if (string.IsNullOrWhiteSpace(lbZipPath.Text))
    {
        MessageBox.Show("Wybierz proszę najpierw folder, który chcesz skompresować");
        return;
    }

    var compressedFileName = $"{Path.GetFileName(lbZipPath.Text)}.zip";

    var zipPath = Path.Combine(_filePath, compressedFileName);

    if (!File.Exists(zipPath))
    {
        MessageBox.Show("Żadne pliki nie zostały jeszcze skompresowane.");
        return;
    }

    Clipboard.SetFileDropList(new StringCollection { zipPath });
}

Tutaj podobnie jak wcześniej najpierw mamy walidację i sprawdzenie, czy po pierwsze ścieżka do folderu została już wybrana i przede wszystkim, czy plik skompresowany istnieje na dysku. Jeżeli warunki nie zostały spełnione, to wyświetlamy komunikat i wychodzimy od razu z tej metody. A jeżeli wszystko jest ok, to wywołujemy statyczną metodę SetFileDropList na klasie Clipboard, przekazując ścieżkę do pliku zip w celu skopiowania nowego pliku.

I w tym momencie nasza pierwsza część zadania jest już gotowa, natomiast zauważ, że mamy tutaj sporo powtarzającego się kod, także dobrą praktyką jest wyodrębnienie go do nowych metod i wywołanie już tylko samych metod. Także zróbmy teraz refaktoryzację, dzięki czemu nasz kod będzie bardziej czytelny i lepszej jakości.
private readonly string _filePath = Path.Combine(Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory), "Files");

public Form1()
{
    InitializeComponent();
    CreateFileDirectory();
}

private void btnChooseFolder_Click(object sender, EventArgs e)
{
    using var folderBrowserDialog = new FolderBrowserDialog();

    var result = folderBrowserDialog.ShowDialog();

    if (result == DialogResult.OK)
    {
        lbZipPath.Text = folderBrowserDialog.SelectedPath;
    }
}

private void btnZipFile_Click(object sender, EventArgs e)
{
    if (!IsZipPathSelected())
    {
        ShowMessageZipPathIsNotSelected();
        return;
    }

    ZipFolder();

    MessageBox.Show("Folder został skompresowany!");
}

private void btnCopyZipFile_Click(object sender, EventArgs e)
{
    if (!IsZipPathSelected())
    {
        ShowMessageZipPathIsNotSelected();
        return;
    }

    var zipPath = GetZipPath();

    if (!File.Exists(zipPath))
    {
        MessageBox.Show("Żadne pliki nie zostały jeszcze skompresowane.");
        return;
    }

    Clipboard.SetFileDropList(new StringCollection { zipPath });
}

private void CreateFileDirectory()
{
    if (!Directory.Exists(_filePath))
        Directory.CreateDirectory(_filePath);
}

private void ZipFolder()
{
    var destinationFileName = GetZipPath();

    if (File.Exists(destinationFileName))
        File.Delete(destinationFileName);

    ZipFile.CreateFromDirectory(lbZipPath.Text, destinationFileName);
}

private void ShowMessageZipPathIsNotSelected()
{
    MessageBox.Show("Wybierz proszę najpierw folder, który chcesz skompresować");
}

private bool IsZipPathSelected()
{
    return !string.IsNullOrWhiteSpace(lbZipPath.Text);
}

private string GetZipPath()
{
    var compressedFileName = $"{Path.GetFileName(lbZipPath.Text)}.zip";
    return Path.Combine(_filePath, compressedFileName);
}

Super. Mamy kilka nowych dobrze nazwanych metod, dzięki czemu kod jest bardziej czytelny i nie mamy już powtórzeń. Dodatkowo dodałem zabezpieczenie przy próbuje kompresji plików. Jeżeli plik, który chcemy utworzyć już wcześniej istniał, to przed samą kompresją go usuwamy. Bez tego zostałby rzucony w takim przypadku błąd.

Możemy teraz przetestować tę część aplikacji.

Przy próbie kliknięcia przycisku Skompresuj pliki lub Kopiuj skompresowany plik (bez wcześniejszego wyboru folderu) otrzymujemy prawidłowo błąd walidacji:

Praca z Plikami ZIP w C#/.NET w Praktyce - 3

Po kliknięciu Wybierz folder otwiera się przeglądarka plików:

Praca z Plikami ZIP w C#/.NET w Praktyce - 4

Po wyborze konkretnego folderu, ścieżka jest wyświetlana na etykiecie.

Praca z Plikami ZIP w C#/.NET w Praktyce - 5

W tym momencie możemy kliknąć przycisk Skompresuj pliki. Pojawia się komunikat:

Praca z Plikami ZIP w C#/.NET w Praktyce - 6

I faktycznie pliki zostały skompresowane. W plikach aplikacji mamy teraz folder Files oraz nowy plik ZipAppTest.zip:

Praca z Plikami ZIP w C#/.NET w Praktyce - 7 Praca z Plikami ZIP w C#/.NET w Praktyce - 8

I po kliknięciu przycisku Kopiuj skompresowany plik, faktycznie plik zip został dodany do schowka.

Wszystko działa, możemy przejść do 2 etapu, czyli rozpakowanie plików.


Rozpakowywanie plików z ZIP (wyodrębnienie)


Na początek zaimplementujemy logikę, która zostanie wywołana po kliknięciu przycisku Wybierz plik .zip.
private void btnChooseZipFile_Click(object sender, EventArgs e)
{
    using var openFileDialog = new OpenFileDialog();
    openFileDialog.InitialDirectory = _filePath;
    openFileDialog.Filter = "Zip files (*.zip)|*.zip";

    var result = openFileDialog.ShowDialog();

    if (result == DialogResult.OK)
    {
        lbUnzipFile.Text = openFileDialog.FileName;
    }
}

Tym razem skorzystamy z klasy OpenFileDialog, dzięki której użytkownik będzie mógł wybrać tym razem konkretny plik. Dodatkowo po otworzeniu dialogu zostanie domyślnie wybrany folder Files naszej aplikacji oraz będziemy mogli wybrać tylko pliki o rozszerzeniu .zip. Jeżeli plik zostanie wybrany, to całą ścieżkę wyświetlimy na etykiecie lbUnzipFile.

Mamy przygotowany wybór pliku, także możemy już zaimplementować samo rozpakowywanie.
private void btnUnzipFile_Click(object sender, EventArgs e)
{
    var selectedPath = lbUnzipFile.Text;

    if (!IsUnzipPathSelected())
    {
        ShowMessageUnzipPathIsNotSelected();
        return;
    }

    if (!File.Exists(selectedPath))
    {
        MessageBox.Show("Wybrany plik nie istnieje.");
        return;
    }

    var destinationDirectoryName = GetUnzipDestinationDirectoryName();

    ZipFile.ExtractToDirectory(selectedPath, destinationDirectoryName, true);

    MessageBox.Show("Wybrany plik został rozpakowany!");
}

private string GetUnzipDestinationDirectoryName()
{
    var selectedPath = lbUnzipFile.Text;

    return Path.Combine(Path.GetDirectoryName(selectedPath), Path.GetFileNameWithoutExtension(selectedPath));
}

private bool IsUnzipPathSelected()
{
    return !string.IsNullOrWhiteSpace(lbUnzipFile.Text);
}

private void ShowMessageUnzipPathIsNotSelected()
{
    MessageBox.Show("Wybierz proszę najpierw plik, który chcesz rozpakować.");
}

Nauczony wcześniejszymi doświadczeniami od razu dodałem kilka prywatnych metod, dzięki czemu unikniemy powtórzeń oraz kod będzie bardziej czytelny. Na początek walidujemy dane, to znaczy sprawdzamy czy plik został wybrany oraz jeżeli został wybrany, to czy jest na dysku. Jeżeli któryś z warunków nie zostanie spełniony, wyświetlamy komunikat i wychodzimy z tej metody. Jeżeli wszystko jest ok, to budujemy ścieżkę pod którą chcemy wypakować wszystkie pliki. Czyli będzie to folder o takiej samej nazwie, jak nazwa pliku zip, tylko oczywiście bez samego rozszerzenia. Następnie dzięki klasie ZipFile oraz metody ExtractToDirectory wypakujemy wszystkie pliki, które były skompresowane. Do metody ExtractToDirectory przekazujemy kolejno ścieżkę do pliku zip, ścieżke do folderu docelowego oraz wskazujemy, że chcemy nadpisać pliki jeżeli będą jakieś konflikty. Na koniec wyświetlimy użytkownikowi komunikat, dzięki czemu będzie wiedział, że wszystko się powiodło.

To tyle jeżeli chodzi o rozpakowanie plików. Natomiast mamy tutaj do zaimplementowania jeszcze jedną funkcję, to znaczy chcemy, żeby po rozpakowaniu, pliki zostały wyświetlone użytkownikowi w eksploratorze plików.
private void btnShowUnzipFiles_Click(object sender, EventArgs e)
{
    if (!IsUnzipPathSelected())
    {
        ShowMessageUnzipPathIsNotSelected();
        return;
    }

    var destinationDirectoryName = GetUnzipDestinationDirectoryName();

    if (!Directory.Exists(destinationDirectoryName))
    {
        MessageBox.Show("Pliki nie zostały jeszcze rozpakowane.");
        return;
    }

    var startInfo = new ProcessStartInfo
    {
        Arguments = destinationDirectoryName,
        FileName = "explorer.exe"
    };

    Process.Start(startInfo);
}

Po wstępnej walidacji danych (podobnie jak wcześniej) skorzystaliśmy tym razem z klasy Process i metody Start, która umożliwia nam uruchomienie dowolnego procesu. Otworzyliśmy proces explorer.exe, czyli eksplorator plików na ścieżce, gdzie znajdują się wszystkie wypakowane pliki. Dzięki temu użytkownik od razu będzie miał do nich dostęp.

Mamy już wszystko. Pozostało tylko przetestować naszą aplikację i zobaczyć czy wszystko działa.

Sprawdźmy czy jeżeli nie zostanie jeszcze wybrany plik zip, to czy możemy kliknąć przycisk Rozpakuj pliku lub Pokaż rozpakowane pliki. Zgodnie z założeniami został wyświetlony błąd walidacji:

Praca z Plikami ZIP w C#/.NET w Praktyce - 9

Po kliknięciu w Wybierz plik .zip wyświetla się przeglądarka plików z zawężeniem do plików .zip:

Praca z Plikami ZIP w C#/.NET w Praktyce - 10

Po wyborze konkretnego pliku zip, kompletna ścieżka zostaje wyświetlona na etykiecie:

Praca z Plikami ZIP w C#/.NET w Praktyce - 11

I możemy teraz kliknąć przycisk Rozpakuj pliki. Po kliknięciu wyświetlił się komunikat:

Praca z Plikami ZIP w C#/.NET w Praktyce - 12

I faktycznie plik zip został rozpakowany. Możemy teraz szybko przejść do tych plików klikając w przycisk Pokaż rozpakowane pliki:

Praca z Plikami ZIP w C#/.NET w Praktyce - 13

Został otworzony eksplorator plików i możemy podejrzeć wszystkie pliki, które wcześniej zostały skompresowane.

Super, wszystko zostało zaimplementowane i działa zgodnie z naszymi oczekiwaniami.

To wszystkie na dzisiaj. Jeżeli taki artykuł Ci się spodobał, to koniecznie dołącz do mojej społeczności – darmowe zapisy, gdzie będziesz również miał dostęp do dodatkowych materiałów i przede wszystkim bonusów. Do zobaczenia w kolejnym artykule.

Poprzedni artykuł - To Musisz Wiedzieć o Serializacji i Deserializacji JSON w C#
Autor artykułu:
Kazimierz Szpin
Kazimierz Szpin
CTO & Founder - FindSolution.pl
Programista C#/.NET. Specjalizuje się w Blazor, ASP.NET Core, ASP.NET MVC, ASP.NET Web API, WPF oraz Windows Forms.
Autor bloga ModestProgrammer.pl
Dodaj komentarz

Wyszukiwarka

© Copyright 2024 modestprogrammer.pl. Wszelkie prawa zastrzeżone. Regulamin. Polityka prywatności. Design by Kazimierz Szpin