November Black Friday C/C++ week
Black Friday je tu! Využij jedinečnou příležitost a získej až 80 % znalostí navíc zdarma! Více zde
Pouze tento týden sleva až 80 % na e-learning týkající se C/C++

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

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

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)
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

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 230x (67.65 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

 

Článek pro vás napsal Michal Žůrek - misaz
Avatar
Jak se ti líbí článek?
2 hlasů
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.
Předchozí článek
Práce s vlastními soubory v C# 1 - Zip archiv
Všechny články v sekci
Práce se soubory v C#
Miniatura
Následující článek
Práce s vlastními soubory v C# 3 - Výjimky a asociace přípon
Aktivity (1)

 

 

Komentáře
Zobrazit starší komentáře (6)

Avatar
Neaktivní uživatel:23.11.2014 20:44

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
Neaktivní uživatelský účet
Avatar
Odpovídá na Neaktivní uživatel
Michal Žůrek - misaz:23.11.2014 20:49

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
Avatar
Neaktivní uživatel:23.11.2014 21:00

Dík

Odpovědět
23.11.2014 21:00
Neaktivní uživatelský účet
Avatar
blazoid
Člen
Avatar
Odpovídá na Neaktivní uživatel
blazoid:6.11.2015 19:35

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
Avatar
Andy Scheuchzer:1.7.2018 21:23

Dělám tuhle aplikaci ve WPF a zaseknul jsem se u RefreshFormData(), teda AktualizujFor­mularovaData(), konkrétně u dosazení obrázku. Zkoušel jsem hledat všude možně a nakonec jsem se se svými začátečnickými znalostmi dostal sem

ObrazekImage.SetValue(DependencyProperty.Register("Bmp", typeof(System.Drawing.Image), typeof(System.Windows.Controls.Image)), Zamestnanec.Foto);

což je teda splácanina podle tohohle a háže mi

System.Argumen­tException: 'Vlastnost Bmp byla již prvkem Image registrována.'

(a vážně nevím na co to asi bude dobré). Jsi schopný mi ještě po tomhle zmatení poradit jak do Image dosadit fotku? :-)

Editováno 1.7.2018 21:23
Odpovědět
1.7.2018 21:23
Člověk, co si myslí, že snědl všechnu moudrost světa, i když tomu tak není.
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Patrik Pastor:12. května 20:33

Chtel bych se zeptat, kdzy autor vytvari xml dokument, a Tvori jednotlive XmlElementy, tak jak to, ze hodnoty jsou THIS.Jmeno...THIS­.Prijmeni... apod. Ja totiz mam slovicko "this" odkaz na Form tridu, ve ktere pisu (a myslim ze i autor), tak jak tedy udelal aby this patrilo Zamestnanci? Jelikoz jsem mel Zamestnance deklarovanoho private (samostatne), tak jsem psat "zamestnanec.Jme­no", "zamestnanec.Pri­jemni" ... Jak tedy autor premenil this na Zamestnance, kdyz pise metodu ve tride Form? (a tedy this by mel byt odkaz na ni, nikoliv na Zamestnance)?

 
Odpovědět
12. května 20:33
Avatar
Odpovídá na Patrik Pastor
Patrik Pastor:12. května 20:39

beru zpet, ted jsem si vsiml, ze metoda je vytvorena ve tride zamestnanec.

 
Odpovědět
12. května 20:39
Avatar
Patrik Pastor:12. května 21:46

Mohl by prosim nekdo rozvest co presne znamena ten filter? Mam namysli
dialog.Filter = Zamestnanec.filter;
Co znamena Zamestnanec.filter? Zamestnanec je instance tridy Zamestnanec, ale neobsahuje string, nebo co presne to vrati (ma to vracet string, ale jaky string Zamestnance)? nebo se jedna o nejakou cestu? Nerozumim presne, jaky string ten Filter vraci

 
Odpovědět
12. května 21:46
Avatar
Patrik Pastor:12. května 23:10

K cemu je vlastne ten druhy using? konkretne System.IO.Com­pression.File­System? implementoval jsem pouze System.IO.Com­pression (bez filesystem), a pouzil tridu ZipFile, tak k cemu je potom ten druhy using (s filesystemem)?

 
Odpovědět
12. května 23:10
Avatar
Patrik Pastor:13. května 20:47

Nevim, zkousel jsem serializovat a hazelo mi to error (viz priloha fotky). Mel jsem vytvoreneho bezparametrickeho kontruktoru Zamestnanec. Dale public class aby se mohlo serializovat. Dale jsem serializoval nikoliv tridu Zamestnance (typ), ale public List<Zamestnanec>, tedy list (i kdyz ted me napada, ze mi to mohlo jit i bez listu - pouze Zamestnance (ale to by prece - v pripade, ze by to byla vetsi aplikace bylo kontraproduktivni, kdyz zamestnanec neni prece jeden). Nevim, jestli to neni pripad, kdy se serializuji kontrloky z Form app, ve ktere je aplikace, ale to mi prijde nepravdepodobne (i kdyz se text(stringy) nacitaji v metode "synchronizace poli", kde dale tyto string Jmeno, Prijmeni,..... Pridavam do listu Add jako novou instanci Zamestnance. I presto mi to vyhodi error: There was an error while serilizeing (bez vetsich detailu). Co bych tedy mohl mit spatne?

 
Odpovědět
13. května 20:47
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 16. Zobrazit vše