Práce s vlastními soubory v C# 2 - Ukládání a načítání zip

C# .NET Práce se soubory Práce s vlastními soubory v C# 2 - Ukládání a načítání zip

V minulém tutoriálu o vlastním formátu souborů jsme si představili možnosti archivů zip a založili projekt s formulářem pro zadání zaměstnance. V tomto díle se naučíme pracovat se složkou ZIP, ukládat a otevírat soubory vlastního typu.

Načítání hodnot do objektu zaměstnance

Nyní musíme do zaměstnance hodnoty z formuláře načíst. Přesuňme se zpátky ke kódu formuláře. Všem textovým polím přidejte stejnou obsluhu události pro KeyUp. V této obsluze dosaďte do zaměstnance hodnoty z textových polí. Metoda zároveň aktualizuje záhlaví okna. KeyUp proto, aby se událost nevyvolávala, když dosazujeme do políčka hodnoty z kódu. To bude jedna z dalších metod.

private void SynchronizacePoli(object sender, KeyEventArgs e)
{
        zamestnanec.Jmeno = txtJmeno.Text;
        zamestnanec.Prijmeni = txtPrijmeni.Text;
        zamestnanec.Email = txtEmail.Text;
        zamestnanec.Telefon = txtTelefon.Text;
        this.Text = txtJmeno.Text + " " + txtPrijmeni.Text;
}

Jediný prvek dtbNarozeni bude využívat události ValueChanged. V obsluze zaměstnanci dosadí svoji hodnotu.

private void dtpNarozeni_ValueChanged(object sender, EventArgs e)
{
        zamestnanec.DatumNarozeni = dtpNarozeni.Value;
}

Nyní budeme potřebovat opak a to nastavit hodnoty ze zaměstnance do formuláře. Toho využijeme když otevřeme soubor se zaměstnancem a budeme ho chtít načíst. Proto si napíšeme metodu AktualizujFor­mularovaData. Metoda dosadí do všech políček hodnoty ze zaměstnance. Metoda opět aktualizuje i záhlaví okna.

private void AktualizujFormularovaData()
{
        txtJmeno.Text = zamestnanec.Jmeno;
        txtPrijmeni.Text = zamestnanec.Prijmeni;
        txtEmail.Text = zamestnanec.Email;
        txtTelefon.Text = zamestnanec.Telefon;
        dtpNarozeni.Value = zamestnanec.DatumNarozeni;
        pcbFoto.Image = zamestnanec.Foto;

        this.Text = txtJmeno.Text + " " + txtPrijmeni.Text;
}

Ukládání souboru

Zaměstnanec bude mít metodu Uloz, která bude přijímat jediný parametr a to cestu k souboru *.zamestnanec. Metoda bude ukládat zaměstnance do zazipované složky se soubory foto.jpg a info.xml. Pro info.xml budeme potřebovat metodu, která vygeneruje xml a zapíše xml do archívu. V metodě budeme dost často pracovat s datovými proudy.

Nejprve si vytvoříme dočasnou složku, kam budeme data ukládat. Ukládat data přímo do archivu by totiž bylo zbytečně problémové. Využijeme metody Path.Combine, kterou zkombinujeme s AppData, složkou pro naši aplikaci a náhodným názvem. Tuto složku vytvoříme.

string cestaTemp = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
        @"zamestnanci\",
        Path.GetRandomFileName());

Directory.CreateDirectory(cestaTemp);

Pomocí konstrukce Using vytvoříme datový proud pro soubor info.xml v naší dočasné složce.

using (FileStream fs = new FileStream(Path.Combine(cestaTemp, "info.xml"), FileMode.Create))
{
        UlozInfo(fs);
}

Tento proud předáme privátní metodě UlozInfo, která do proudu zapíše XML.

private void UlozInfo(Stream proud)

Je na vás jestli XML zapíšete pomocí SAX, DOM nebo budete serializovat. V tomto článku to ani nebudu popisovat detailně. Můj kód exportu vypadá následovně.

XmlDocument doc = new XmlDocument();

XmlElement zamestnanci = doc.CreateElement("Zamestnanci");
XmlElement zamestnanec = doc.CreateElement("Zamestnanec");
zamestnanci.AppendChild(zamestnanec);
XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", "utf-8", null);
doc.AppendChild(dec);
doc.AppendChild(zamestnanci);

XmlElement Jmeno = doc.CreateElement("Jmeno");
Jmeno.AppendChild(doc.CreateTextNode(this.Jmeno));

XmlElement Prijmeni = doc.CreateElement("Prijmeni");
Prijmeni.AppendChild(doc.CreateTextNode(this.Prijmeni));

XmlElement Email = doc.CreateElement("Email");
Email.AppendChild(doc.CreateTextNode(this.Email));

XmlElement Telefon = doc.CreateElement("Telefon");
Telefon.AppendChild(doc.CreateTextNode(this.Telefon));

XmlElement DatumNarozeni = doc.CreateElement("DatumNarozeni");
DatumNarozeni.AppendChild(doc.CreateTextNode(this.DatumNarozeni.ToShortDateString()));

zamestnanec.AppendChild(Jmeno);
zamestnanec.AppendChild(Prijmeni);
zamestnanec.AppendChild(Email);
zamestnanec.AppendChild(Telefon);
zamestnanec.AppendChild(DatumNarozeni);

doc.Save(proud);

Nyní se vraťme k metodě Uloz. Vytvoříme další stream, tentokrát pro foto.jpg. Obrázek uložíme jeho metodou Save, která přijímá datový proud a výstupní formát. Díky výstupního formátu jsme schopni obrázek převést. Ačkoliv třeba uživatel vloží obrázek jako BMP, vždy bude v archivu uložen jako JPG.

using (FileStream fs = new FileStream(Path.Combine(cestaTemp, "foto.jpg"), FileMode.Create))
{
        this.Foto.Save(fs, ImageFormat.Jpeg);
}

Zipování složky v C# .NET

.NET Framework 4.5 a výš obsahuje třídy pro práci se zipováním souborů. Jsou ve jmenném prostoru System.IO.Com­pression. Abyste však tyto jmenné prostory viděli, musíte si přidat k projektu 2 reference. V Solution Exploreru klikněte pravým tlačítkem na References > Add New Reference.

Visual Studio přidat referenci

Do vyhledávacího políčka napište System.IO a označte reference System.IO.Com­pression a System.IO.Com­pression.File­System.

Přidat refernce System.IO.Compression

Dialog potvrďte. Do Using si přidejte odkazy na tyto knihovny.

Třída ZipFile má statickou metodu CreateFromDirec­tory, která přijímá dva parametry. První parametr je cesta ke složce, která má být zazipována a druhý je výsledný soubor. Tento soubor nemusí mít nutně příponu zip a toho využijeme. Víceméně však pouze metodě dosadíme cesty.

ZipFile.CreateFromDirectory(cestaTemp, soubor);

Vraťme se do formuláře. Vytvořte událost Click na objektu btnUlozit. Opět budeme vytvářet dialog. Protože filtr pro příponu zaměstnance použijeme 2× (otvírání a ukládaní), dáme ji třídě Zamestnanec jako statický člen.

public static string filter = "Soubor zaměstnance (*.zamestnanec)|*.zamestnanec";

Dialogu dosadíme výchozí název souboru, který bude vycházet ze jména a příjmení zaměstnance. Dialog otevřeme. Pokud vrátí OK, zaměstnance uložíme.

private void btnUlozit_Click(object sender, EventArgs e)
{
        SaveFileDialog dialog = new SaveFileDialog();
        dialog.Filter = Zamestnanec.filter;
        dialog.FileName = zamestnanec.Jmeno + " " + zamestnanec.Prijmeni;
        if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
                zamestnanec.Uloz(dialog.FileName);
        }
}

Aplikaci si spusťte. Vyberte nějakou fotku, vyplňte textová pole a nějaké datum. Zkuste zaměstnance uložit. Zkuste změnit jeho příponu na zip a podívejte se dovnitř. Bude tam fotka a soubor info.xml.

Ukázka vyexportovaného zaměstnance

Otevíraní souborů v aplikaci

Otevíraní souboru budeme řešit v druhém přetíženém konstruktoru. Do konstruktoru nám v parametru bude předána cesta k souboru se zaměstnancem. Z tohoto souboru potřebuje získat datový proud, ten předáme v konstrukci Using konstruktoru třídy ZipArchive.

public Zamestnanec(string soubor)
{
        using (FileStream fs = new FileStream(soubor, FileMode.Open))
        {
                using (ZipArchive zip = new ZipArchive(fs))
                {

                }
        }

}

ZipArchive má metodu GetEntry, která vrátí ZipArchiveEntry. Ten obsahuje soubor uložený v archivu. ZipArchiveEntry má pak metodu Open, která vrací datový proud k souboru. Info.xml budeme načítat v metodě NactiInfo, které opět předáme datový proud a ta zpracuje XML.

ZipArchiveEntry info = zip.GetEntry("info.xml");
NactiInfo(info.Open());

NactiInfo můžete zase napsat více způsoby, já jsem použil DOM. Můj kód vypadá následovně.

private void NactiInfo(Stream proud)
{
        XmlDocument doc = new XmlDocument();
        using (XmlReader r = XmlReader.Create(proud))
        {
                doc.Load(r);
                this.Jmeno = doc.GetElementsByTagName("Jmeno")[0].FirstChild.Value;
                this.Prijmeni = doc.GetElementsByTagName("Prijmeni")[0].FirstChild.Value;
                this.Email = doc.GetElementsByTagName("Email")[0].FirstChild.Value;
                this.Telefon = doc.GetElementsByTagName("Telefon")[0].FirstChild.Value;
                this.DatumNarozeni = DateTime.Parse(doc.GetElementsByTagName("DatumNarozeni")[0].FirstChild.Value);
        }
}

Fotku načteme podobně, z datového proudu obrázek dostaneme pomocí statické metody Image.FromStream.

ZipArchiveEntry foto = zip.GetEntry("foto.jpg");
this.Foto = Image.FromStream(foto.Open());

Vraťme se k formuláři a vytvořte obsluhu události Click pro tlačítko otevřít. Opět vytvoříme dialog a opět mu načteme filtr. Otevřeme ho a porovnáme návratovou hodnotu s OK. Pokud uživatel soubor vybere, vytvoříme nového zaměstnance a v konstruktoru mu předáme cestu k souboru. Nakonec aktualizujeme data na formuláři, aby se změny ze zaměstnance projevily i na formuláři. Celá metoda by mohla vypadat následovně.

private void btnNacist_Click(object sender, EventArgs e)
{
        OpenFileDialog dialog = new OpenFileDialog();
        dialog.Filter = Zamestnanec.filter ;
        if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
                zamestnanec = new Zamestnanec(dialog.FileName);
                AktualizujFormularovaData();
        }
}

Nyní je aplikace hotová, můžete vyzkoušet.

Načtení zaměstnance

Aplikaci pořádně vyzkoušejte, příště se podíváme na možná vylepšení. Ošetříme chyby a vyšperkujeme soubory v průzkumníkovi Windows. V kódu pod článkem máte na testování přiloženého i plně validního zaměstnance Jana Nováka.


 

Stáhnout

Staženo 171x (67.65 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

  Aktivity (1)

Článek pro vás napsal Michal Žůrek (misaz)
Avatar
Autor se věnuje tvorbě aplikací pro počítače, mobilní telefony, mikroprocesory a tvorbě webových stránek a webových aplikací. Nejraději programuje ve Visual Basicu a TypeScript. Ovládá HTML, CSS, JavaScript, TypeScript, C# a Visual Basic.

Jak se ti líbí článek?
Celkem (2 hlasů) :
4.54.54.54.54.5


 



 

 

Komentáře

Avatar
Milan Křepelka
Redaktor
Avatar
Milan Křepelka:

Ahoj. Mám dvě věci, které je potřeba nebrat jako kritiku, spíše námět "jiné" cesty.

To zpracování z/do XML udělat méně pracněji. XML serializací objektu dosáhneš téhož, méně pracněji a nebudeš muset měnit kód pokaždé když se změní struktura objektu.

Když už máš to tak pěkně zapouzdřený do zaměstnance je imo škoda nevyužít výhody obousměrného databindingu. Pak by ses obešel bez těch metod nahrávajících data z/do zaměstnanec<->formulář. Ale abych řekl pravdu, velmi často sem to dělal podobným způsobem.

 
Odpovědět 5.7.2014 13:19
Avatar
Odpovídá na Milan Křepelka
Michal Žůrek (misaz):

tak XMl si může zpracovat každý jak chceš, metodu UlozInfo jsem nechával otevřenou, tudiž si to každý může implementovat jak chce, to není až zas takový problém. Článk byl zaměřen, tak aby to demonstroval i uživateli s naprostými základy formulářových aplikací a navíc mám pocit že u WinForms tu ty tutoriály ani binding nepopisují.

Odpovědět 5.7.2014 13:30
Nesnáším {}, proto se jim vyhýbám.
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na Michal Žůrek (misaz)
Jan Vargovský:

Tím líp, když bys to ukázal, jak se to dělá.

 
Odpovědět 7.7.2014 1:13
Avatar
Lukáš Tesař:

Dá se tato aplikace upravit nějak aby ji bez problémů spustili
uživatelé windows 7 a to i bez doinstalovaného .NETu 4.5? (Můj případ
to zrovna není, ale chtěl bych udělat program, který by těchto
vlastních souborů využíval a zároveň bez problémů jel na .NET 3.5.)
Existuje tedy nějaká náhrada tříd

System.IO.Compression

a

System.IO.Compression.FileSystem

?

Editováno 18.11.2014 21:40
 
Odpovědět 18.11.2014 21:38
Avatar
Odpovídá na Lukáš Tesař
Michal Žůrek (misaz):

existuje 3rd party knihovna, kterou jsem používal předtím než to zavedli přímo do .NET. Konkrétně to byla SharpLibZip http://icsharpcode.github.io/SharpZipLib/ .

// Btw. původní verze článku tuto knihovnou využívala.

Odpovědět 18.11.2014 22:44
Nesnáším {}, proto se jim vyhýbám.
Avatar
Lukáš Tesař:

A jak se SharpZipLib implementuje do projektu? Nikdy jsem nic takového nepřidával. Mimochodem děkuji za pohotovou odpověď.

 
Odpovědět 23.11.2014 20:44
Avatar
Odpovídá na Lukáš Tesař
Michal Žůrek (misaz):

musíš přidat referenci a pak můžeš normálně používat, tak jak je to popsané v dokumentaci. Stáhneš to DLLko, v Solution Exploreru klikneš na references pravým > Add Reference > najdeš stažené DLLko > vybereš. A pak si v dokumentaci najdeš v jakém namenspace to je a jak se to používá.

Odpovědět 23.11.2014 20:49
Nesnáším {}, proto se jim vyhýbám.
Avatar
 
Odpovědět 23.11.2014 21:00
Avatar
blazoid
Člen
Avatar
Odpovídá na Lukáš Tesař
blazoid:

Ahoj, jelikož mám MSV2010, předpokládám, že nepodporuje FrameWork 4.5 (i když balík mám na PC nainstalován) - tudíž mám též potíže s přidáním reference System.IO.Com­pression.File­System. Jdou funkce tohoto FrameWork nějakým způsobem v tomto visual studiu implementovat?

 
Odpovědět 6.11.2015 19:35
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 10.