ASP.NET pro zaΦßteΦnφky
5. Prßce se soubory
MENU

Mo₧nost uchovßvat data je velikou v²hodou serverov²ch aplikacφ a mnoho tv∙rc∙ strßnek tou₧φ po t∞chto aplikacφch prßv∞ kv∙li tomu, aby si mohli naprogramovat vlastnφ knihu nßvÜt∞v nebo anketnφ systΘm, co₧ jim standardnφ HTML nenabφzφ.

Mßme mnoho mo₧nostφ. Dnes se ve velkΘ mφ°e pou₧φvajφ databßze, jako je MS SQL, My SQL nebo Access. Pro mΘn∞ navÜt∞vovan² web je vÜak programovßnφ databßzov²ch aplikacφ celkem zbyteΦnΘ - tvo°it questbook na osobnφ strßnce pomocφ MySQL je jako jφt s kan≤nem na vrabce.

Co bychom tedy mohli pou₧φt? T°eba obyΦejnΘ soubory. Prßce s nimi je jednoduchß a snadno pochopitelnß, majφ vÜak velk² problΘm - m∙₧e k nim p°istupovat jen jeden program zßrove≥. Co₧ m∙₧e zp∙sobit problΘmy, hlavn∞ pokud je vßÜ web hodn∞ navÜt∞vovan². V tΘ chvφli ji₧ ale budete stejn∞ pot°ebovat v²kon∞jÜφ prost°edky v podob∞ databßzφ.

Intermezzo: Zßklady objektov∞ orientovanΘho programovßnφ

V tΘto lekci se poprvΘ setkßvßme s BCL (Basic Class Library) .NET Frameworku a budeme vyu₧φvat jeho objekt∙. Bylo by tedy vφce ne₧ vhodnΘ osv∞tlit si zßklady objektov∞ orientovanΘho programovßnφ (b∞₧n∞ pou₧φvanß zkratka je OOP).

Zßkladnφm prvkem OOP je objekt. Co to je? Objekt je shluk metod, vlastnostφ a udßlostφ. Pokud chceme v OOP vyjßd°it n∞jakou strukturu, vytvo°φme pro ni objekt. OOP je elegantnφ prßv∞ dφky tomu, ₧e lidskß mysl p°irozen∞ myslφ a °eÜφ problΘmy skrze objekty.

P°edlohou pro objekt je t°φda. T°φda je forma, z kterΘ bude objekt vytvo°en. T°φdou naz²vßme vlastnφ programov² k≤d, objekt vznikne, kdy₧ zavolßme konstruktor danΘ t°φdy.

Co to je? Uka₧me si to na p°φklad∞:

Dim alena As osoba = new osoba("Alena", "Novßkovß", 38)

V tomto p°φpad∞ jsme vytvo°ili objekt alena z t°φdy osoba, kterΘ jsme hned do zaΦßtku p°edali n∞kolik ·daj∙. Z pohledu programßtora t°φdy je konstruktor blok k≤du, kter² se starß o p°ijmutφ t∞chto n∞kolika ·daj∙, z pohledu programßtora aplikace vyu₧φvajφcφho objekt m∙₧eme konstruktorem nazvat obsah zßvorky.

Nynφ mßme objekt alena... Co s nφm? Objekt m∙₧e mφt svΘ vlastnosti. To jsou hodnoty, kterΘ si dan² objekt dr₧φ po dobu svΘ existence.

alena.v∞k = alena.v∞k + 1

Objekty majφ obvykle metody. To jsou kusy k≤du, kterΘ vykonßvajφ urΦitou Φinnost. Znßte je mo₧nß pod jmΘny procedura nebo funkce.

alena.najezSe()

Objekty majφ svoje udßlosti. To se t²kß hlavn∞ rozhranφ mezi u₧ivatelem a programem - u₧ivatel vyvolßvß udßlost, kterou objekt oÜet°uje.

Vra¥me se k metodßm a konstruktor∙m. Co kdy₧ chceme vytvo°it vφce mo₧nostφ, jak danou metodu zavolat? T°eba soubor m∙₧eme naΦφst bu∩ s pomocφ jeho cesty nebo p°φmo zφskßnφm datovΘho proudu. Proto tu existuje mo₧nost p°et∞₧ovßnφ.

alena.najdiSiPrßci(aleninPoΦφtaΦ1)
nebo
alena.najdiSiPrßci(·°adPrßce)

P°et∞₧ovat jdou i konstruktory. To u₧ je snad jasnΘ. DalÜφ zajφmavou vlastnostφ, kterou OOP nabφzφ, je d∞diΦnost. Jedna t°φda m∙₧e tzv. d∞dit t°φdu jinou. To znamenß, ₧e p°evezme vÜechny metody, vlastnosti a udßlosti jinΘ t°φdy. Sama pak m∙₧e p°idßvat vlastnφ t°φdy nebo upravovat existujφcφ. T°φda, kterß d∞dφ, je v∞tÜinou specißlnφm p°φpadem t°φdy, kterß je d∞d∞na. Tak₧e t°eba StreamWriter (objekt pro zapisovßnφ do soubor∙ skrze datovΘ proudy) je specißlnφm p°φpadem Streamu (objekt pro prßci s datov²mi proudy).

Tak, toto zßkladnφ obeznßmenφ by vßm mohlo pro tentokrßt staΦit. M∙₧eme se pustit do...

Prßce s textov²mi soubory

Pus¥me se do toho. Co je to vlastn∞ soubor? B∞₧n∞ se tφmto termφnem myslφ kolekce dat, kterß je ulo₧ena na pevnΘm disku. Programßtor za tφmto termφnem vidφ mφsto, kam si jeho aplikace m∙₧e ulo₧it data na delÜφ ΦasovΘ obdobφ, nejen po dobu spuÜt∞nφ programu nebo zpracovßvßnφ strßnky.

Dv∞ma nejpou₧φvan∞jÜφmi t°φdami v souvislosti s textov²mi soubory bude StreamReader a StreamWriter. Oba jsou potomci t°φdy Stream a podle toho s nimi budeme pracovat. Stream lze do ΦeskΘho jazyka p°elo₧it jako proud. A opravdu, s textov²m souborem naΦten²m do StreamReaderu budeme pracovat jako s kontinußlnφm proudem dat.

╚tenφ soubor∙

ZaΦneme se Φtenφm soubor∙. Jak takovß prßce se StreamReaderem vypadß? Nejd°φve musφme vytvo°it instanci danΘho objektu. Pamatovat p°i nφ musφme na to, ₧e tato t°φda mß p°etφ₧en² kontruktor, tudφ₧ ji m∙₧eme volat r∙zn²mi zp∙soby. NejobvyklejÜφ jsou tyto:

Instanci StreamReaderu jsme vytvo°ili... Co dßl? Nynφ mßme k dispozici metody Read a ReadLine, kterΘ pou₧ijeme pro vlastnφ Φtenφ souboru.

Metoda Read je p°etφ₧enß, ve verzi bez parametr∙ vracφ jeden znak ve tvaru celΘho Φφsla (int32), druhß verze je o n∞co slo₧it∞jÜφ. Jako vstupy se bere pole znak∙ (char), celΘ Φφslo, kterΘ oznamuje, na kterΘm indexu v poli znak∙ se mß zaΦφt zapisovat a dalÜφ celΘ Φφslo, kterΘ oznaΦuje maximßlnφ mo₧n² poΦet naΦten²ch znak∙. Po provedenφ tohoto p°etφ₧enφ tΘto funkce se zadanΘ pole naplnφ znaky ze souboru.

Metoda ReadLine je myslφm pou₧φvan∞jÜφ a u₧iteΦn∞jÜφ. Nemß ₧ßdnΘ parametry - prost∞ p°eΦte jeden °ßdek souboru a vrßtφ jeho obsah. Jeden °ßdek je v souboru ukonΦen specißlnφm znakem, kter² m∙₧eme v programu zapsat jako \n (to jeÜt∞ vyu₧ijeme, a₧ budeme chtφt n∞jak "zcivilizovat" HTML k≤d generovan² serverem).

Mo₧nß bych se jeÜt∞ mohl zmφnit o metod∞ ReadToEnd. P°eΦte vÜechna data od aktußlnφ pozice kurzoru v souboru a vrßtφ je.

╚asto budeme pot°ebovat p°eΦφst veÜker² obsah souboru po °ßdcφch. Jak ale zjistφme, ₧e u₧ jsme Φtenφ ukonΦili (₧e u₧ se kurzor nachßzφ na konci souboru)? K tomu se skv∞le hodφ metoda Peek. Vrßtφ nßsledujφcφ znak, ale nepohne s kurzorem v souboru. Pokud nenφ ₧ßdn² dalÜφ znak dostupn² (narazili jsme na konec souboru), vrßtφ -1. Cel² soubor obsah souboru tedy po °ßdcφch vypφÜeme tφmto zp∙sobem:

Do While SR.Peek() <> -1
  Response.Write(SR.ReadLine() + "<br>")
Loop

Elegantnφ, nenφ-li₧ pravda? V tomto p°φpad∞ ale existuje elegantn∞jÜφ metoda s pou₧itφm funkce ReadToEnd.

Response.Write(SR.ReadToEnd().Replace("\r", "").Replace("\n", "<br>"))

Kurzor je na zaΦßtku souboru - metoda ReadToEnd p°eΦt∞ soubor cel². Jak ale zajistit p°evedenφ od°ßdkovßnφ v souboru do od°ßdkovßnφ v HTML k≤du? Metoda Replace je p°φstupnß pro ka₧d² String, kter²m v²stup funkce ReadToEnd nepochybn∞ je. Ve stringu vyhledß a nahradφ jeden °et∞zec jin²m. Tak₧e nejd°φv zruÜφme znak \r, kter² se obΦas pro od°ßdkovßnφ kombinuje s \n a potom samotn² \n zm∞nφme na <br>. Metodu Replace si zapamatujte, je velmi u₧iteΦnß.

Pozor! V₧dy po skonΦenφ prßce se souborem doporuΦuji zavolat jeho metodu Close(). Ta uzav°e soubor, se kter²m jsme pracovali. Pokud ji nezavolßme, soubor z∙stane otev°en² a p°φÜtφ pokus o otev°enφ skonΦφ chybovou hlßÜkou, ₧e dan² soubor je pou₧φvßn jin²m procesem. Je mo₧nΘ upravit vlastnost FileShare u objektu FileStream, ale p°esto je vhodn∞jÜφ ka₧d² soubor v₧dy zav°φt.

Zßpis do soubor∙

Dostßvßme se k t°φd∞ StreamWriter. Je v mnohΘm podobnß StreamReaderu, mß i velmi podobnΘ konstruktory, tak₧e je snad ani nebude t°eba vysv∞tlovat.

Podobn∞ jako StreamReader mß i tato t°φda funkci Write a WriteLine. Write je op∞t p°etφ₧enß. Tentokrßt Φty°ikrßt:

A pak je tu samoz°ejm∞ WriteLine. M∙₧e vßs zmßst, ₧e na MSDN Library v referenci t°φd BCL tato metoda uvedena nenφ. Nenφ to vÜak zp∙sobeno nedostatky v dokumentaci, spφÜ tφm, ₧e vßm "zamlΦuji" n∞kterß mΘn∞ d∙le₧itß fakta. T°eba to, ₧e metoda StreamWriter (StreamReader je na tom obdobn∞) ned∞dφ p°φmo Stream, ale t°φdu TextWriter, kterß se stejn∞ jako StreamWriter nachßzφ ve jmennΘm prostoru System.IO. Tato t°φda obsahuje mnohΘ funkce, vΦetn∞ WriteLine.

Nebudu zde vypisovat vÜechna p°etφ₧enφ - je jich hodn∞. Zapisovat toti₧ mimo typu String jdou i dalÜφ, jako je boolean, integer, decimal atp. Nßm bude ale zatφm bohat∞ staΦit znßt p°etφ₧enφ StreamWriter.WriteLine(String).

I zde musφm zd∙raznit nutnost pou₧φvat metodu Close() p°i ukonΦenφ prßce se souborem.

Kniha nßvÜt∞v

Nynφ se pustφme do malΘho opakovßnφ vÜeho, co jsme se zatφm o tvorb∞ webov²ch strßnek pomocφ technologie ASP.NET nauΦili. Pokusφme se sestavit primitivnφ knihu nßvÜt∞v. Jist∞ jste u₧ n∞kde na Internetu takovou aplikaci vid∞li - webmaster ji v∞tÜinou dßvß na svoje strßnky s lichou nad∞jφ, ₧e mu nßvÜt∞vnφci napφÜφ n∞jakou konstruktivnφ kritiku jeho webu. Nebudeme se zatφm sna₧it o n∞jakou velkou funkcionalitu - nap°φklad o WYSIWIG editaci p°φsp∞vk∙ nebo o threadovou diskuzi se zatφm pokouÜet nebudeme. A₧ se nauΦφme databßze, znovu se k tomutu projektu vrßtφme.

VÜechny p°φsp∞vky budeme uklßdat - jak jinak - do textovΘho souboru. Co °ßdek souboru, to p°φsp∞vek do diskuze. Formßt jednoho °ßdku (p°φsp∞vku) bude nßsledujφcφ:

datum a Φas p°idßnφ#u₧ivatelovo jmΘno#mailovß adresa#vlastnφ p°φsp∞vek

Je tu ovÜem otßzka, jak tyto p°φsp∞vky v souboru °adit. Toti₧ jestli mß b²t nejnov∞jÜφ p°φsp∞vek zapisovßn na zaΦßtek nebo na konec souboru. Pokud budeme novΘ p°φsp∞vky p°idßvat na konec, bude zapsßnφ novΘho u₧ivatelova post°ehu trvat malou chviliΦku - m≤dem p°φstupu Append m∙₧eme na konec souboru p°φsp∞vky pohodln∞ p°idßvat. Na druhou stranu pro zobraznφ prvnφ strßnky knihy nßvÜt∞v budeme muset dojet a₧ na konec tohoto souboru, proto₧e v tomto p°φpad∞ je zvykem zobrazovat p°φsp∞vky od nejnov∞jÜφho po nejstarÜφ. Pokud se rozhodneme p°idßvat novΘ p°φsp∞vky na zaΦßtek souboru, budeme pohodln∞ Φφst, ale Üpatn∞ zapisovat (budeme muset zapsat cel² obsah souboru do pole, soubor vymazat, p°idat nov² zßpis a zapsat p∙vodnφ obsah souboru z pole).

Vzhledem k tomu, ₧e budeme pravd∞podobn∞ mnohem Φast∞ji zobrazovat ne₧ Φφst, rozhodl jsem se k druhΘ variant∞ a komplikovanΘmu zapisovßnφ soubor∙.

DalÜφ otßzkou je, jestli v tomto p°φpad∞ pou₧φt pokroΦilou funkcionalitu serverov²ch ovlßdacφch prvk∙. Myslφm si, ₧e pro tuto ·lohu se moc nehodφ, proto₧e tyto prvky jsou zam∞°eny na zφskßvßnφ ·daj∙ z databßze. Tedy p°esn∞ji °eΦeno z objektu DataSet, kter² si m∙₧eme bez problΘm∙ naplnit z databßze sami. Stßle si ale myslφm, ₧e pro tak jednoduchou webovou aplikaci nebude takov²ch prost°edk∙ t°eba. Pou₧ijeme je pouze pro vytvo°enφ formulß°e pro zφskßnφ nov²ch p°φsp∞vk∙. Vylastnφ v²pis knihy budeme provßd∞t p°es serverov² ovlßdacφ prvek Label.

Pus¥me se do toho! Nezalekn∞te se tohoto k≤du, je to kompletnφ zßpis celΘho programu a podrobn∞ ho vysv∞tlφm.

01 <%@ Page Language="VB" Debug="True" %>
02 <%@ Import Namespace="System.IO" %>
03 <%@ Import Namespace="System.Text" %>
04
05 <script runat="server">
06    Private adresa As String = "d:/localhost/questbook.txt"
07  Sub Page_Load()
08    Dim SR As StreamReader = new StreamReader(adresa)
09
10    Dim kniha As String = ""
11    Dim parsRadek() As String
12    Do While SR.Peek <> -1
13      parsRadek = SR.ReadLine.Split("#")
14      kniha = kniha + zobrazPrispevek(parsRadek(0), parsRadek(1), 
                        parsRadek(2), parsRadek(3), parsRadek(4))
15    Loop
16    
17    SR.Close()
18    
20    popisek.Text = kniha
21  End Sub
22  
23  
24
25  Sub novyPrispevek(obj As object, e As EventArgs)
26    Dim zalSoubor(100) As String
27    
28    Dim SR As StreamReader = new StreamReader(adresa)
29    Dim i As Integer = 0
30    Do While (SR.Peek <> -1 AND i < 100)
31      zalSoubor(i) = SR.ReadLine()
32      i = i + 1
33    Loop
34    
35    SR.Close()
36    
37    Dim soubor As FileStream = File.Open(adresa, FileMode.Create)
38    Dim SW As StreamWriter = new StreamWriter(soubor)
39    
40    
41    Dim datum As String = Now.ToString("dd. mm. yyyy HH:MM")
42    SW.WriteLine(datum + "#" + uzjmen.Value.Replace("#","") +
           "#" + mailad.Value.Replace("#","") + "#" +
           predmet.Value.Replace("#","") + "#" + obsah.Value.Replace("#",""))
43
44    For q As Integer = 0 To i - 1
45      SW.WriteLine(zalSoubor(q))
46    Next q
47    
48    SW.Close()
49    
50    popisek.Text = zobrazPrispevek(datum, uzjmen.Value.Replace("#",""),
           mailad.Value.Replace("#",""), predmet.Value.Replace("#", ""),
           obsah.Value.Replace("#","")) + popisek.Text
51  End Sub
52  
53  
54  
55  Function zobrazPrispevek(datum As String,uzjmen as String,
                             mailad As String, predmet As String, obsah As String)
56    Dim SB As StringBuilder = new StringBuilder()
57    SB.Append("<small>" + datum + "</small>   ")
58    SB.Append("<b>" + uzjmen + "</b> 
               (<a href=""mailto:" + mailad + """>" + 
               mailad + "</a>)<br>")
59    SB.Append("<big><b>" + predmet + "</b></big><br>")
60    SB.Append(obsah + "<br><br>")
61    return SB.ToString()
62  End Function
63</script>
64
65<form runat="server" action="questbook.aspx" method="post">
66  VaÜe jmΘno: <input type="text" runat="server" id="uzjmen" /><br>
67  Mailovß adresa: <input type="text" runat="server" id="mailad" /><br>
68  <b>P°edm∞t</b>: <input type="text" runat="server" id="predmet" /><br>
69  <textarea id="obsah" runat="server"> </textarea><br>
70  <button id="cudlik" onServerClick="novyPrispevek" runat="server">OdeÜli!</button>
71</form>
72
73<asp:Label id="popisek" runat="server" />
74
75<a href="questbook.aspx">Obnovit</a>

Po ·vodnφch direktivßch p°ichzφ jako prvnφ metoda Page_Load. Na ÜestΘm °ßdku se deklaruje prom∞nnß adresa, do kterΘ si ulo₧φme (p°ekvapiv∞) adresu naÜeho souboru s obsahem diskuze. Ka₧dß jejφ zm∞na pak bude jednoduÜÜφ. Nastupuje metoda Page_Load, kterß si nejd°φve otev°e nßÜ soubor (8) a zaΦne postupn∞ naΦφtat jednotlivΘ °ßdky. Mohli bychom se pozastavit u funkce Split. To je velmi u₧iteΦnß v∞c, kterß nßm textov² °et∞zec "rozsekß" na kusy. "Mφsta °ezu" jsou voleny na mφstech se zadan²m znakem nebo °et∞zcem. Tak₧e nap°φklad p°φkaz:

   dim vysledek() as String
   vysledek = "abaqobac".Split("b")

ulo₧φ do prom∞nnΘ vysledek pole s t°emi prvky {"a", "aqo", "ac"}. Pokud zavolßte tuto funkci s parametrem "" (prßzdn² °et∞zec), vyjde vßm za v²sledek pole jednotliv²ch znak∙ p∙vodnφho Stringu. Opravdu velmi u₧iteΦnΘ.

Vra¥me se k naÜemu programu. Funkce Split je tu urΦena k rozd∞lenφ °ßdku souboru na jednotlivΘ polo₧ky odd∞lenΘ znakem "#". V dalÜφm kroku cyklu si zavolßme funkci zobrazPrispevek. ProΦ je to funkce? Proto₧e stejnou operaci pot°ebujeme provΘst i v metod∞ novyPrispevek a bylo by nepraktickΘ psßt stejn² k≤d na dv∞ mφsta. Tato funkce vyu₧φvß t°φdy System.Text.StringBuilder. Ta nßm umo₧≥uje elegantn∞ "slo₧it" °et∞zec bez oÜkliv²ch blok∙ "vysledek = vysledek + ...". V tΘto funkci postupn∞ vytvo°φme HTML k≤d jednoho p°φsp∞vku a vrßtφme ho. P∙vodnφ cyklus ho p°ipojφ do prom∞nnΘ kniha.

Ta je nßsledn∞ vypsßna (20) a soubor uzav°en (17). To bylo docela jednoduchΘ, ne?

Nßsleduje funkce novyPrispevek. Ta na °ßdcφch 26 - 35 otev°e soubor a naΦte vÜechny p°φsp∞vky v n∞m do prom∞nnΘ. Kdyby do naÜφ diskuze nßhodou p°iÜlo vφce ne₧ sto ohlas∙, program nehavaruje (dφky podmφnce v p°φkazu while), ale o°φzne nejstarÜφ p°φsp∞vek. Dßle je otev°en soubor a dφky parametru FileMode.Create bude p°edchozφ obsah souboru vymazßn. Jako prvnφ poslΘze zapφÜeme nov² p°φsp∞vek a pomocφ funkcφ Replace dßvßme pozor na to, aby se nßm do souboru nevloudil dalÜφ znak "#" - to by nßm rozhodilo strukturu jednoho °ßdku souboru.

Na °ßdku 41 si m∙₧ete vÜimnout zajφmavΘ v∞ci - program vezme aktußlnφ datum a provede funkci ToString s jak²msi textov²m parametrem. To je dalÜφ ·₧asnß v∞c - tφmto zp∙sobem m∙₧ete datum p°evΘst do prakticky libovolnΘho formßtu. Parametry viz SDK a MSDN.

Na °ßdcφch 44 - 46 zapisujeme do souboru jeho p∙vodnφ obsah. Na °ßdku 50 p°idßvßme k obsahu Labelu popisek jeÜt∞ nejnov∞jÜφ p°φsp∞vek, kter² by se jinak neprojevil a u₧ivatel by tak mohl b²t zmaten.

Zßv∞rem

Tak, dneÜnφ lekce vßm doufßm dodß dostatek materißlu k p°em²Ülenφ na cel² m∞sφc. NßÜ GuestBook je samoz°ejm∞ nedokonal². Minimßln∞ nabφdnout mo₧nost strßnkovßnφ by bylo u₧iteΦnΘ.

V p°φÜtφ lekci se budeme v∞novat konfiguraΦnφm soubor∙m web.config a global.asax. Povφme si takΘ n∞co o mo₧nostech cachovßnφ. A zkusφme dßle vylepÜit Guestbook.

LukßÜ Lßnsk²
VeÜkerΘ nßm∞ty, dotazy a p°ipomφnky piÜte na adresu lansky@czech-ware.net.