A leckében megismerkedünk a multimédiás tartalmak kezelésével többrétegű webes környezetben. Az általános ismeretek bemutatása után megnézzük, hogy PHP és MySQL környezetben hogyan oldható meg ezen tartalmak feltöltése, tárolása és megjelenítése, végül pedig egy példaalkalmazáson keresztül nézzük meg ennek gyakorlati megvalósulását.
A leckét elolvasva elvárható, hogy a hallgató képes legyen webes alkalmazását multimédiás elemeket kezelő funkcióval ellátni.
Az előző részekben keveset szóltunk a multimédiás tartalmak kezeléséről, holott képek, hangok, videók egyre nagyobb mértékben jelennek meg webes alkalmazásokban. Azért kell ezzel külön foglalkozni a webadatbázis-programozáson belül, mert a nagyméretű bináris adatok kezelése az összes szinten (kliens, szerver, adatbázis) eltér attól, mint ahogy az elemi numerikus és kis szöveges adatokat kezelni kell.
Az adatbázisok a nagyméretű objektumokat (angolul ‘large object’ vagy LOB) megkülönböztetik a szerint, hogy az objektumban szöveges vagy bináris információ található, pontosabban az objektumot szövegként vagy bináris tartalomként kezeljék. A nagyméretű szöveges állományokra CLOB-ként (Character Large Object, azaz karakteres nagy objektumként) hivatkoznak, míg a multimédiás elemeket ún. nagyméretű bináris objektumokként (angolul ‘binary large object’) szokták kezelni, és az angol megnevezés rövidítése alapján BLOB-oknak hívják. Programozói oldalon ezt a különbséget az interfészek már nem teszik meg, így vagy LOB-ként vagy BLOB-ként hivatkoznak rájuk.
BLOB-okkal elsősorban tehát akkor találkozhatunk, ha képet, hangot vagy videót szeretnénk alkalmazásunkban kezelni. Egy tipikus folyamat a feltöltéssel kezdődik, amelyet űrlapon keresztül tudunk elvégezni. Az űrlap enctype attribútumát multipart/form-data értékűre kell állítani, az űrlapon belül pedig egy <input type="file"> mezővel tudjuk a feltöltő mezőt kirakni. Egy példa űrlap így nézhet ki:
<form action="url" method="post" class="big" enctype="multipart/form-data"> <input type="file" name="ujkep" /> <input type="submit" name="feltolt" value="Feltöltés" /> </form>
A webszerveren a feltöltött fájlok átmenetileg tárolásra kerülnek, ezek tudjuk az adott környezet megfelelő interfészével elérni (PHP-ban a $_FILES[] tömb elemeként). A fájlok innentől kétféleképpen kerülhetnek tárolásra: vagy fájlként leteszik őket a webszerver kijelölt mappájába, vagy adatbázisba mentik el őket. Az előbbi eset technikailag csupán egy másolás, az utóbbi esetben az átmeneti fájl tartalmát a megfelelő adatbázismezőbe kell továbbítani. Mivel a fájlok nagyok lehetnek, ezért ebben az esetben a fájl adatait folyamként kell az adatbázis felé küldeni. Alkalmazásfüggő, hogy melyik megoldást választjuk, mindkettőnek vannak előnyei és hátrányai:
Fájlok tárolása fájlrendszerben:
Előnyök | Hátrányok |
|
|
Fájlok tárolása adatbázisban:
Előnyök | Hátrányok |
|
žAz adatbázis könnyen nagy méretűvé válhat.
|
A tárolás után folytatva a fájlok kezelésével kapcsolatos elemzést a következő feladat a fájlok kliensre küldése. Ha a fájl adatbázisban tárolt, akkor nemcsak a webszerver és a kliens, de az adatbázis-szerver és a webszerver között is megfelelően kell a nagy állományokat közlekedtetnünk. Bonyolultabb esetben itt összetettebb folyamkezelésre is szükség lehet. A kliens felé azonban a fájlokat nem elég egyszerűen kiíratnunk, hanem a böngészőnek jelezni kell, hogy miféle tartalom érkezik. Ezek az információk a tartalomhoz kötődnek, de a tartalommal együtt nem továbbíthatók. A HTTP protokoll szerint ezeket az információkat HTTP fejlécekben kell küldenünk. PHP-ban ilyen fejlécet küldeni a header() paranccsal tudunk, de csak az előtt, hogy bármiféle tartalmat a kliens felé továbbítani kezdtünk volna. A fejlécben többek között a következő információkat adhatjuk meg:
Az alábbi példa a fejlécek használatát mutatja be PHP-ban. A szkript egy táblázatot generál, amelyet Excel állományként tűntet fel a kliens számára:
<?php header("Content-Type: application/xls"); //header("Content-Length: "); header("Accept-Ranges: bytes"); header("Content-Disposition: attachment; filename=exceltabla.xls"); header("Content-Description: Alkalmazás Excel Export"); ?> <table> <tr> <td colspan="2">alma</td><td>banan</td> </tr> <tr> <td>alma<br>korte</td> </tr> <tr> <td>alma</td><td>banan</td> </tr> </table>
A következőkben megnézzük, hogy PHP-ban milyen lehetőségeink vannak BLOB-ok tárolására MySQL adatbázisban.
MySQL-ben a nagy méretű bináris adatok tárolására a BLOB típus szolgál. Négyféle BLOB típus van: TINYBLOB, BLOB, MEDIUMBLOB és LONGBLOB. Ezek a bennünk tárolható adat maximális hosszában különböznek. Nagy méretű szöveges adatok tárolására a TEXT típus szolgál, és ebből is négyféle található: TINYTEXT, TEXT, MEDIUMTEXT és LONGTEXT. A BLOB és a TEXT típusok csak abban különböznek, hogy az adatbázis hogyan veszi figyelembe a bennük tárolt adatokat. A BLOB típus egyszerű byte-sorozatként tekint rá (byte szöveg), míg a TEXT típus a byte-sorozatokra egy adott kódolás szerinti karaktersorozatként tekint, így lehetővé válik az ilyen adatok rendezése, összehasonlítása is.
Példaalkalmazásunkban a kep tábla tartalmaz BLOB mezőket: a bemutatóhoz tartozó kép és a belőle készült bélyegkép tárolására (ld. az alkalmazás tervezése fejezetet). Az alábbiakban a kep nevű mezőbe próbálunk egy képfájlt feltölteni. A táblák közötti megkötések, valamint nem NULL oszlopok miatt a bemutato_id és a sorszam mezők is töltésre kerülnek. A fájl nevét és a MIME típusát a kepfajl és a mime_tipus mezőben tároljuk.
Ugyan elavult bővítményről van szó, nézzük azért meg, hogy a mysql bővítménnyel hogyan lehet fájlok tárolását adatbázisban megoldani. Ezt az interfészt még nem készítették fel speciálisan BLOB-ok használatára, így ebben az esetben egy BLOB típusú mező töltése és kezelése ugyanúgy történik, mint egy szöveges (pl. VARCHAR) mező kezelése. A fájlt egy az egyben be kell olvasni egy PHP változóba szövegként, majd ezt a változót kell a lekérdezés megfelelő paraméterének helyére illeszteni. A beolvasást a file_get_contents() függvénnyel érdemes elvégezni, hiszen ez nemcsak a szöveges, hanem a bináris állományokat is megbízhatóan kezeli és tárolja szövegként. Az SQL utasítást az sprintf() függvénnyel rakjuk össze, és ne feledjük a szöveges paramétereinkre, így a fájlt tartalmazó változónkra is a mysql_real_escape_string() függvényt meghívni. Az így összeállított INSERT parancsot pedig a mysql_query() függvénnyel futtassuk.
Az alábbi példában egy függvényben van a feltöltő logika. A függvény paraméterül kapja a feltöltendő fájl elérhetőségét ($tmpname), a fájl nevét ($filename), valamint a MIME típusát ($type).
<?php function save_to_mysql($tmpname, $filename, $type) { $filedata = file_get_contents($tmpname); $conn = mysql_connect('localhost', 'dyss', 'jelszo'); mysql_select_db('dyss', $conn); mysql_query('set names utf8'); $q = sprintf( "INSERT INTO kep (kep, kepfajl, sorszam, bemutato_id, mime_tipus) VALUES ('%s', '%s', 0, 1, '%s')", mysql_real_escape_string($filedata), mysql_real_escape_string($filename), mysql_real_escape_string($type) ); $result = mysql_query( $q ); mysql_close($conn); } ?>
Lekérdezéskor hasonlóan járunk el, hiszen a fájl a lekérdezéskor is szöveges típusként kezelendő. A lekérdezést a mysql_query() függvénnyel futtatjuk, majd az eredményt a mysql_fetch_assoc() függvénnyel töltjük be egy tömb típusú változóba, amelynek a megfelelő oszlopa tartalmazza a fájlunkat egy jó hosszú szöveg formájában. Kliensre küldéshez ezt az oszlopot kell kiírni az echo paranccsal, de előtte érdemes HTTP fejlécben leküldeni az állomány típusát, hogy a böngésző megfelelő módon tudja azt kezelni. Ennek hiányában sokszor egy kép sem jelenik meg az <img> tagben, mivel a PHP-ból érkező adatfolyamot a webszerver automatikusan szöveges információként jelzi a kliens felé. Ezt pontosítandó kell nekünk a megfelelő fejlécet küldeni a bináris tartalom mellé. Ezt az információt használja fel a böngésző egyébként annak eldöntésére is, hogy az adott tartalmat hogyan, milyen pluginnel, külső programmal jelenítse meg.
Az alábbi példában GET paraméterként kapjuk meg a kép adatbázisbeli azonosítóját, ezzel kérdezünk be az adatbázisba, és utána küldjük le a képet a kliensre:
<?php $id = (int)$_GET['id']; $conn = mysql_connect('localhost', 'dyss', 'jelszo'); mysql_select_db('dyss', $conn); mysql_query('set names utf8'); $q = sprintf( "SELECT kep, kepfajl, mime_tipus FROM kep WHERE id = %d", $id ); $result = mysql_query( $q ); $sor = mysql_fetch_assoc($result); header('Content-Type: ' . $sor['mime_tipus']); echo $sor['kep']; mysql_free_result($result); mysql_close($conn); ?>
Ha a képet nem megjeleníteni szeretnénk, hanem letöltésre felajánlani, akkor a fenti header() utasítás után még egy HTTP fejlécet le kell küldenünk:
<?php header('Content-Type: ' . $sor['mime_tipus']); header('Content-Disposition: attachement; filename=' . $sor['kepfajl']); ?>
A mysql bővítmény nagy hátránya, hogy a fájlokat egy szöveges változóba be kell olvasnunk, és csak azon keresztül tudjuk az adatbázisba betölteni vagy lekérdezni. Ez azt jelenti, hogy az adott szkript memóriaigénye nagyon megnövekedhet, hiszen akár több megabyte-os fájlokat is változókban tárol. Megjegyezzük, hogy ez a módszer még a korszerűbb interfészeken keresztül is működik, hiszen maga a MySQL adatbázis képes értelmezni az SQL utasításokban szereplő hosszú szövegeket BLOB-ként. A következőkben azt nézzük meg, hogy a korszerűbb interfészek hogyan tudják a BLOB-ok kezelését hatékonyabban megoldani.
A mysqli bővítmény sok egyéb funkciója mellett a BLOB-ok kezelését is lehetővé teszi. Ehhez az előkészített utasításokat kell használnunk. (E nélkül a mysql bővítménynél megismert módszer működik csak.) Az előkészített utasításokban paraméterkötésnél (mysqli_stmt::bind_param()) lehetőségünk van jelezni, hogy az adott paramétert BLOB-ként kezelje (b betűvel). Ezután a mysqli_stmt::send_long_data() függvény segítségével kisebb csomagokban fel tudjuk tölteni az állomány tartalmát az adatbázisoldalon előkészített paraméter értékeként. Ezek után már csak az utasítást kell futtatnunk (mysqli_stmt::execute()), és készen is vagyunk.
A lenti példában láthatjuk, hogy hogyan történik a paraméterkötés. A mysqli_stmt::bind_param() függvényben b betűvel kell jelezni, hogy az adott paramétert BLOB-ként kezelje. Mivel itt konkrét változót nem tudunk hozzákötni, ezért ezt a szükséges elvárást egy NULL értékkel elégítjük ki. Azonban a mysqli_stmt::bind_param() függvény paraméterként referencia szerint átadott változót vár, így közvetlenül nem írhatjuk be a NULL értéket. Ezért azt kiemeltük egy $null nevű változóba. A lényeg azonban ezután kezdődik. A fájlt a szokásos fájlműveletekkel 8 kB-os darabokban beolvassuk, és minden egyes darabot a mysqli_stmt::send_long_data() függvénynek adunk át. Ennek a függvénynek első paramétereként szükséges jelezni, hogy melyik paraméterbe töltse fel az adatot. A paraméterek indexelése 0-tól kezdődik. A feltöltést elvégezve futtathatjuk is az előkészített utasításunkat.
<?php function save_to_mysqli($tmpname, $filename, $type) { $mysqli = new mysqli('localhost', 'dyss', 'jelszo', 'dyss'); $mysqli->query('set names utf8'); $q = 'INSERT INTO kep (kep, kepfajl, sorszam, bemutato_id, mime_tipus) VALUES (?, ?, 0, 1, ?)'; $stmt = $mysqli->prepare($q); $null = NULL; $stmt->bind_param('bss', $null, $filename, $type); $f = fopen($tmpname, "r"); while (!feof($f)) { $stmt->send_long_data(0, fread($f, 8192)); } fclose($f); $result = $stmt->execute(); $stmt->close(); $mysqli->close(); } ?>
Lekérdezéskor a mysqli bővítmény esetében sincsen lehetőség a BLOB-okat folyamként kezelni, vagy darabokban letölteni. Előkészített utasítások esetén a lekérdezett oszlopokat a mysqli_stmt::bind_result() függvénnyel kötjük változókhoz, amikbe a mysqli_stmt::fetch() utasítás tölti be az értékeket. Ebben a folyamatban sehol nem lehet megmondani, hogy bizonyos oszlop BLOB-ként viselkedjen. Az így adatkötött változóban tehát megjelenik a fájl tartalma, amit egy az egyben írathatun ki az echo paranccsal.
<?php $id = (int)$_GET['id']; $mysqli = new mysqli('localhost', 'dyss', 'jelszo', 'dyss'); $mysqli->query('set names utf8'); $q = 'SELECT kep, kepfajl, mime_tipus FROM kep WHERE id = ?'; $stmt = $mysqli->prepare($q); $stmt->bind_param('i', $id); $stmt->execute(); $stmt->store_result(); $stmt->bind_result($kep, $filename, $mime_tipus); $stmt->fetch(); header('Content-Type: ' . $mime_tipus); echo $kep; $stmt->close(); $mysqli->close(); ?>
Letöltésre itt is a korábban megismert fejléc segítségével lehet felajánlani az állományt.
A legegyszerűbb és egyszersmind legsokoldalúbb programozói felületet a PDO biztosítja számunkra. Itt is előkészített utasításokat kell használnunk, különben egyedül a mysql bővítménynél megismert módszer marad. Előkészített utasításnál a PDOStatement::bindParam() függvény explicit használatával kell jelezni a harmadik paraméterben, hogy az adott változót BLOB-ként szeretnénk kezelni. Ebben az esetben az adott változónak valamilyen folyamszerűségnek kell lennie: ehhez vagy a PHP folyamkezelő megoldását használjuk (Streams kiegészítő), vagy ilyen például egy megnyitott fájl is. Az adatkötés előtt tehát olvasásra meg kell nyitnunk a fájlunkat, majd adatkötés után futtatni kell az SQL utasítást. A PDO a háttérben maga gondoskodik az adott folyam hatékony feltöltéséről.
A példában láthatjuk ennek gyakorlati megvalósítását. A fentiekhez képest annyit érdemes megjegyezni, hogy a fájlt 'rb' opcióval nyitottuk meg a binárisan kezelhetőség miatt.
<?php function save_to_pdo($tmpname, $filename, $type) { $dbh = new PDO('mysql:host=localhost;dbname=dyss', 'dyss', 'jelszo'); $dbh->exec('set names utf8'); $q = 'INSERT INTO kep (kep, kepfajl, sorszam, bemutato_id, mime_tipus) VALUES (:kep, :kepfajl, 0, 1, :mime_tipus)'; $stmt = $dbh->prepare( $q ); $f = fopen($tmpname, 'rb'); $stmt->bindParam(':kep', $f, PDO::PARAM_LOB); $stmt->bindParam(':kepfajl', $filename, PDO::PARAM_STR); $stmt->bindParam(':mime_tipus', $type, PDO::PARAM_STR); $result = $stmt->execute(); $dbh = null; } ?>
Lekérdezéskor a PDO elvileg lehetőséget ad BLOB-ok folyamszerű visszaadására. Ehhez az előkészített utasítások eredménykötésére van szükségünk, ahol a lekérdezés mezőit PHP változókhoz kötjük. Ehhez a PDOStatement::bindColumn() függvényt kell használnunk, és jeleznünk kell, hogy az adott oszlopot BLOB-ként kezelje. A változók feltöltése a PDOStatement::fetch() függvény hívásakor történik meg. Ez a függvény paraméter nélkül tömbként adja vissza az eredményhalmaz következő sorát, ebben az esetben viszont a PDO::FETCH_BOUND paraméterrel jeleznünk kell, hogy az eredményhalmaz sorait a megfelelő változókba töltse. A BLOB-nak jelzett változót pedig folyam-, vagy fájlműveletekkel (pl. az fpassthru() függvénnyel) továbbíthatjuk a kliens felé. Sajnos a PHP bizonyos verzióiban nem folyamot, hanem szöveget kapunk vissza (azaz ott vagyunk, ahol a mysql vagy a mysqli bővítmény esetén). Ekkor nem fájlművelettel, hanem a kiíró utasítással küldjük a kliensnek az adatot.
Az alábbi példában ennek gyakorlati megvalósítása látható.
<?php $id = (int)$_GET['id']; $dbh = new PDO('mysql:host=localhost;dbname=dyss', 'dyss', 'jelszo'); $dbh->exec('set names utf8'); $q = 'SELECT kep, kepfajl, mime_tipus FROM kep WHERE id = ?'; $stmt = $dbh->prepare( $q ); $stmt->execute(array($id)); $stmt->bindColumn(1, $kep, PDO::PARAM_LOB); $stmt->bindColumn(2, $filename, PDO::PARAM_STR); $stmt->bindColumn(3, $mime_tipus, PDO::PARAM_STR); $stmt->fetch(PDO::FETCH_BOUND); header('Content-Type: ' . $mime_tipus); //fpassthru($kep); echo $kep; $dbh = null; ?>
A fenti információk szemléltetésére az alább letölthető példaalkalmazás szolgál.
Az ebben található példakód különböző technológiával ugyanazt a funkcionalitást próbálja biztosítani: egy űrlapról feltölteni egy képet, azt a képet szerveroldalon eltárolni, majd megjeleníteni a felületen, ahol mindemellett letöltésre is felajánlja. Az alkalmazás a következő részekből áll:
Az alábbiakban ezeknek a fontosabb részleteit mutatjuk be.
Az űrlap az upload_form.php állományban helyezkedik el, és gyakorlatilag ennek az egyszerű kis alkalmazásnak a nézet rétegéért felel.
<?php if (!empty($uzenetek)) : ?> Üzenetek: <ul> <?php foreach($uzenetek as $uzenet) : ?> <li><?php echo $uzenet; ?></li> <?php endforeach; ?> </ul> <?php endif; ?> <?php if ($kep) : ?> A feltöltött kép: <br /> <img src="<?php echo $src; ?>" /> <br /> <a href="<?php echo $href; ?>">Letölt</a> <?php endif; ?> <h3>Új kép feltöltése</h3> <form action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="post" accept-charset="utf-8" class="big" enctype="multipart/form-data"> <dl> <dt><label for="ujkep">File</label></dt> <dd><input type="file" name="ujkep" value="" id="ujkep" class="text" /></dd> <dd><input type="submit" name="feltolt" value="Feltöltés" class="button" /></dd> </dl> </form>
Három részből áll. Az első elágazás a feldolgozás során keletkező üzenetek megjelenítésére szolgál. A második elágazás a kép megjelenítéséért felelős. Természetesen ha nincs üzenet, vagy nincs kép (pl. az első betöltéskor, vagy hibaüzenetkor), akkor ezek nem jelennek meg. A harmadik rész pedig maga az űrlap, ahogy ezt ennek a fejezetnek az elején is megismerhettük.
Ez az űrlap a nézete mind a négy technológiával megvalósított alkalmazásnak.
A vezérlő a vezerlo.php állományban kapott helyet. Feladat, hogy egyrészt eldöntse, történt-e egyáltalán feltöltés, vagy először hívjuk be az oldalt (első elágazás). Ha feltöltés törént, azt onnan tudjuk, hogy a $_FILES szuperglobális tömb nem üres. Ekkor megtesszük a megfelelő ellenőrzéseket: megfelelő űrlapmező került-e kitöltésre, nem történt-e hiba a feltöltés során, a megadott fájl valóban feltöltött fájl-e (is_uploaded_file() függvénnyel). A hibákat az uzenetek tömbben gyűjtjük. Ha nem volt hiba, akkor a modellnek előkészítjük a fontosabb információkat (a feltöltött fájl átmeneti helye, a fájl neve és MIME típusa), majd azokat átadjuk a modellfüggvénynek. A vezérlő logikája technológiától függetlenül ugyanaz, egyedül a modellfüggvény változik, így ezt paraméterként kapja meg, és a call_user_func() függvénnyel hívja meg. A vezérlő két információval tér vissza a technológiafüggő vezérlőbe, az üzenetekkel, valamint a mentett fájl azonosítójával.
<?php function vezerlo($fgv) { $uzenetek = array(); $mezonev = 'ujkep'; $id = NULL; if ($_FILES) { if (!$_FILES[$mezonev]) { $uzenetek[] = 'Nem megfelelő a mező neve.'; } else { if ($_FILES[$mezonev]['error'] != 0 || !is_uploaded_file($_FILES[$mezonev]['tmp_name'])) { $uzenetek[] = 'Hiba történt a feltöltés során.'; } } if (empty($uzenetek)) { $tmpname = $_FILES[$mezonev]['tmp_name']; $filename = basename($_FILES[$mezonev]['name']); $type = $_FILES[$mezonev]['type']; if ($id = call_user_func($fgv, $tmpname, $filename, $type)) { $uzenetek[] = 'File sikeresen feltöltve.'; } else { $uzenetek[] = 'Hiba történt a mentés során.'; } } } return array( 'uzenetek' => $uzenetek, 'id' => $id, ); } ?>
Az egyes technológiafüggő vezérlők az alábbi állományokban vannak:
Ezek tartalmazzák a megfelelő modellfüggvényt, hívják a fenti vezérlőt, és készítik elő az adatokat a nézet számára.
Az upload_file.php tartalmazza a fájlrendszerbe mentés kódját. Ahogy azt fentebb már általánosságban írtuk, ekkor gyakorlatilag egy másolásról van szó az átmeneti helyről a fájl végleges helyére. A fájlok végleges helyét egy files/ könyvtárban tűztük ki, a másoláshoz pedig a PHP move_uploaded_file() függvényét használtuk. Ebben az esetben a fájl azonosítója maga az elérési útvonala, amit a $filename változó tartalmaz.
<?php function save_to_file($tmpname, $filename, $type) { $konyvtar = 'files/'; $filename = $konyvtar . $filename; if (move_uploaded_file($tmpname, $filename)) { return $filename; } return false; } ?>
Az adatbázisba mentést háromféleképpen (mysql, mysqli és PDO bővítménnyel) oldottuk meg, ezeknek a logikáját az upload_mysql.php, upload_mysqli.php és az upload_pdo.php fájl tartalmazza. A bennük foglaltak lényege e részben már korábban elhangzott.
A kép megjelenítését egy egyszerű <img> elem végzi el, aminek a src attribútumát kell megfelelően beállítani. Legegyszerűbb a teendőnk fájlrendszerbeli tárolás esetén, hiszen ilyenkor egyszerűen azt az útvonalat kell megadni az img-nek, ahol a fájl a szerveroldalon megtalálható:
<img src="files/kep.jpg" />
Adatbázisbeli tárolás esetén azonban itt egy olyan PHP szkript hívására van szükség, amely az adatbázisból lekérdezi a megfelelő képállományt, és azt a kliensnek a böngészőnek leküldi. Tipikusan tehát valahogy így néz ki a src attribútum:
<img src="kerem_a_kepet.php?id=12" />
A megfelelő képre az adatbázisbeli azonosítójával hivatkozunk, azt adjuk át GET paraméterként.
A megjelenítésért felelős PHP kódrészletekkel már fentebb találkoztunk, ezeket a show_mysql.php, show_mysqli.php és a show_pdo.php állomány tartalmazza. A letöltés adatbázisból hasonló módon történik a plusz fejléc bevezetésével. Ezeket a download_mysql.php, download_mysqli.php és a download_pdo.php állomány tartalmazza. Egyedül a fájlrendszerbeli letöltést nem néztük meg, ebben az esetben PHP szkripttel kell felolvasnunk a fájlt a fájlrendszerből és a megfelelő fejlécekkel a kliensnek továbbítani (download_file.php):
<?php $filename = $_GET['path']; //header('Content-Type: ' . mime_content_type($filename)); header('Content-Disposition: attachement; filename=' . basename($filename)); readfile($filename); ?>