Virtuální disk – souborová nástěnka v PHP podruhé

28. listopadu 2002

Na četné žádosti čtenářů vydáváme aktualizovaný článek, který řeší některé specifické problémy, jakým je třeba možnost uploadu spustitelných skriptů. Naše aplikace umožňuje ukládat soubory na webový server, kde si je návštěvníci stránek mohou stáhnout.

Základní aplikace umožní komukoli soubory nahrávat, stahovat, ale i mazat. Díky využití databáze je možné si výpis souborů snadno setřídit podle názvů souborů, podle počtu stažení nebo podle data vložení. Ke každému souboru je také připojen komentář.

Aplikace se hodí pro intranet, pro použití v internetu by bylo vhodné oddělit část pro vkládání a mazání souborů od části, ve které je možné soubory stahovat. Přihlášení je potom možné vyřešit pomocí HTTP Autentifikace. Pro lepší představu si vyzkoušejte ukázku (k dispozici je i zdrojový kód). V aplikaci jsou využity postupy, které již byly popsány zde na Intervalu, proto vynechávám detailní vysvětlování principu funkce.

Struktura tabulky databáze:

CREATE TABLE vdisk
(
filename varchar(50) NOT NULL default “,
comment text NOT NULL,
filesize int(20) NOT NULL default ‚0‘,
dloaded int(11) NOT NULL default ‚0‘,
filetime datetime NOT NULL default ‚0000-00-00 00:00:00‘,
PRIMARY KEY (filename),
UNIQUE KEY filename (filename)
)

Naše aplikace bude využívat hlavní skript index.php a podpůrné skripty (ve složce handle). Potřebná nastavení uložíme souborem config.php:

<?php
$now = gmdate(‚D, d M Y H:i:s‘) . ‚ GMT‘;
header(‚Expires: 0‘); // rfc2616 – Section 14.21
header(‚Last-Modified: ‚.$now);
header(‚Cache-Control: no-store, no-cache, must-revalidate‘); // HTTP/1.1
header(‚Cache-Control: pre-check=0, post-check=0, max-age=0‘); // HTTP/1.1
header(‚Pragma: no-cache‘); // HTTP/1.0
set_time_limit(90);
set_magic_quotes_runtime(1);
$dbappname=“;
$store_files=‘./files/‘;
$sfextension=‘.src‘;
?>

Zde se nastaví odeslání hlaviček, které zamezí kešování dokumentu. Dále zde prodloužíme maximální povolenou dobu běhu skriptu (v případě potřeby můžeme ještě zvýšit, více jak 200 bych asi nedával), nastavíme automatické vkládání zpětných lomítek před nebezpečné znaky, nastavíme proměnnou, která slouží jako předpona tabulky databáze (v naší ukázce je prázdná) a cestu ke složce, ve které budou ukládány soubory na serveru.

Hlavní index.php skript může vypadat podobně jako tento v naší ukázce, který vkládá soubory předávané parametrem v URL:

<?php
require(‚handle/config.php‘);
require(‚handle/opendb.php‘);
if (empty($_GET[‚app‘]))
  $_GET[‚app‘]=’vdisk‘;
?><html>
<head>
<meta http-equiv=“Content-Type“ content=“text/html; charset=iso-8859-2″>
<title>Virtuální disk</title>
</head>
<body>
<?php include(‚handle/‘.$_GET[‚app‘].‘.php‘) ?>
<br /><br />
<a href=“index.php?app=vdisk&amp;sort=<?php echo $_GET[‚sort‘] ?>“>VDisk</a>
<a href=“index.php?app=vdisk_upload&amp;sort=<?php echo $_GET[‚sort‘] ?>“>Nový soubor</a>
</body>
</html>

Skript je jen ukázkou, ošetření parametru pro include je potřeba provést mnohem důkladněji. Povšimněte si odkazů, které jsou vypisovány na konci stránky. Je předáváno nejen jméno skriptu, ale také parametr sort, který bude popsán dále.

Formulář pro upload souboru vdisk_upload.php:

<?php
if (empty($_FILES[‚soubor‘][‚name‘]))
{
?>
<script language=“Javascript“ type=“text/javascript“>
function Control(theForm)
{
  for(var i=0; i<theForm.length; i++ )
  with(theForm.elements[i])
  {
    if( value.length==0)
   {
     alert(‚Zadejte potřebné údaje!‘);
     focus();
     return false;
    }
  }
  return true;
}
</script>
<fieldset>
<legend>Nahrát soubor <br /></legend>
<form id=“upload“ action=“index.php?app=vdisk_upload&amp;sort=<?php echo $_GET[‚sort‘] ?>“ method=“post“ enctype=“multipart/form-data“ onSubmit=“return Control(this)“>
Soubor:<br />
<input type=“file“ name=“soubor“ size=“32″ /><br />
Popis<br />
<textarea name=“comment“ rows=“5″ cols=“40″></textarea><br />
<input type=“Submit“ value=“Upload“ /><br />
</form>
</fieldset>
<?php
}
else
{
  $mess=’Soubor a informace o něm byly uloženy<br />‘;
  if (File_Exists ($store_files.$_FILES[‚soubor‘][‚name‘].$sfextension))
    $mess=’Soubor ‚.$_FILES[‚soubor‘][‚name‘].‘ je již uložen na disku!<br />‘;
  else
  {
    if (@move_uploaded_file($_FILES[‚soubor‘][‚tmp_name‘], $store_files.$_FILES[‚soubor‘][‚name‘].$sfextension))
    {
      if (!@mysql_query(„INSERT INTO „.$dbappname.“vdisk VALUES (‚“.$_FILES[‚soubor‘][‚name‘].$sfextension“‚,'“.$_POST[‚comment‘].“‚,'“.$_FILES[‚soubor‘][‚size‘].“‚,0,NOW())“))
        $mess=’Soubor je uložen na disku, ale informace o něm se nezdařilo uložit!<br />‘;
    }
    else
      $mess=’Soubor se nezdařilo uložit na disk!<br />‘;
?>
Název: <?php echo $_FILES[‚soubor‘][‚name‘].$sfextension ?><br />
Velikost: <?php echo ($_FILES[‚soubor‘][‚size‘]) ?>&nbsp;B<br />
Typ: <?php echo $_FILES[‚soubor‘][‚type‘] ?><br />
Popis: <?php echo $_POST[‚comment‘] ?><br />
<?php
  }
  echo $mess;
}
?>

Při zadávání údajů do formuláře je pomocí JavaScriptu kontrolováno vyplnění polí. Po úspěšném uploadu souboru z formuláře na server uložíme údaje do tabulky databáze. Ve skriptu jsou ošetřeny chyby, které mohou nastat: chyba při uploadu souboru, chyba při ukládání souboru do správné složky a chyba při zápisu do databáze. Naopak není kontrolován typ souboru a jeho velikost, je možné uložit libovolný typ souboru a teoreticky libovolně velký. Prakticky je velikost souboru omezena nastavením PHP (obvykle je povolená hodnota mezi 2 a 8 MB), kromě toho není protokol HTTP vhodný pro velké soubory. Vzhledem k zamýšlenému nasazení v intranetu a také pro zjednodušení zde není ošetřeno vkládání HTML kódu do komentáře souboru. Z bezpečnostních důvodů se k názvu uploadovaného souboru přidává přípona (nastavitelná v config.php), v našem případě .src. Jde o jednoduchou ochranu před možným uploadem proveditelných .php souborů, případný útočník tak nemůže nahrát na server skript, který si třeba vypíše obsah adresáře a poté i obsah souboru s připojením k databázi!

Seznam souborů zobrazuje hlavní skript vdisk.php:

<script language=“JavaScript“ type=“text/javascript“>
<!–
function EraseOK()
{
  delyes=confirm(‚Opravdu chcete soubor nenávratně odstranit?‘)
  if (delyes)
  {
    return true;
  }
  return false;
}
function DLoadInfo()
{
  alert(‚Po stažení přejmenujte soubor odstraněním přípony „<?php echo $sfextension ?>“‚);
}
// –>
</script>
<table border=“0″ cellpadding=“4″ cellspacing=“0″>
<tr>
<th><a href=“index.php?app=vdisk&amp;sort=0″>soubor</a></th>
<th><a href=“index.php?app=vdisk&amp;sort=1″>stažen</a></th>
<th><a href=“index.php?app=vdisk&amp;sort=2″>velikost</a></th>
<th><a href=“index.php?app=vdisk&amp;sort=3″>vložen</a></th>
</tr>
<?php
switch ($_GET[‚sort‘])
{
  case 0:
  $order = ‚filename‘;
  break;
  case 1:
    $order = ‚dloaded DESC‘;
  break;
  case 2:
    $order = ‚filesize DESC‘;
  break;
  case 3:
    $order = ‚filetime DESC‘;
  break;
  default:
    $order = filename;
}
$soubory = @MySQL_Query(„SELECT filename, comment, dloaded, filesize, UNIX_TIMESTAMP(filetime) as time FROM „.$dbappname.“vdisk ORDER BY $order“);
while ($data=@MySQL_Fetch_Array($soubory))
{
?>
<tr>
<td valign=“top“><img src=“images/file.gif“ align=“left“ width=“27″ height=“32″ border=“0″ alt=““ />
[<a href=“index.php?app=vdisk_delete&amp;file=<?php echo StripSlashes($data[‚filename‘]) ?>“ title=“odstranit“ onClick=“return EraseOK()“>x</a>] [<a href=“vdownload.php?file=<?php echo StripSlashes($data[‚filename‘]) ?>“title=“stáhnout soubor“ onClick=“DLoadInfo()“><?php echo StripSlashes($data[‚filename‘]) ?></a>]<br />
<small><?php echo StripSlashes($data[comment]) ?></td>
<td valign=“top“><?php echo $data[dloaded] ?>x</td>
<td valign=“top“><?php echo round($data[filesize]/1024,2) ?> kB</td>
<td valign=“top“><?php echo date(‚d.m.Y h.i‘,$data[‚time‘]) ?></td>
</tr>
<tr><td colspan=“4″><hr></td></tr>
<?php
}
?>
</table>
<?php include(‚handle/vdisk_upload.php‘) ?>

Skript načte do pole obsah tabulky databáze se soubory a vypíše je do tabulky ve stránce. Nadpisy v záhlaví tabulky jsou odkazy, kterými je možné volit způsob setřídění seznamu souborů. Podle předaného parametru sort se ve skriptu zvolí část SQL dotazu (sekvence switch), který určuje setřídění. Vypsané jméno souboru je zároveň odkazem na skript ke stažení souboru. Před názvem souboru je ještě vypsán znak „x“, který je odkazem pro smazání souboru. Smazání souboru je nutno potvrdit v dialogu, který je řešen javaskriptem. Javaskriptem je také doplněna nápověda pro stahování souboru. Povšimněte si funkce stripslashes, která je použita pro výpis komentáře souboru, a také zaokrouhlení vypisované velikosti souboru (zobrazována v kB, údaj v databázi je vydělen konstantou 1024). Na konci skriptu můžeme ještě pomocí include zařadit formulář pro upload souborů.

Pro mazání souborů použijeme vdisk_delete.php:

<?php
$mess = ‚Nebyl určen soubor k odstranění!<br />‘;
if (!empty($_GET[‚file‘])) {
  $mess = ‚Soubor ‚.$_GET[‚file‘].‘ byl odstraněn.<br />‘;
  if (@MySQL_Query(„DELETE FROM „.$dbappname.“vdisk WHERE filename ='“.$_GET[‚file‘].“‚ LIMIT 1″))
  {
    if (!mysql_affected_rows())
    $mess=’Soubor ‚.$_GET[‚file‘].‘ nebyl nalezen!<br />‘;
    elseif (!@unlink($store_files.$_GET[‚file‘]))
      $mess = ‚Soubor ‚.$_GET[‚file‘].‘ byl vyjmut ze seznamu, ale nepodařilo se jej odstranit!<br />‘;
  }
  else
  $mess = ‚Soubor ‚.$_GET[‚file‘].‘ se nepodařilo vyjmout ze seznamu!<br />‘;
  }
echo $mess;
?>

Skript otestuje, zda je zadáno jméno souboru, který se má vymazat. Pokud je zadán, odstraní se z databáze, pokud proběhlo odstranění z databáze korektně, provede se vymazání souboru ze složky na serveru. Pokud proběhlo řádně i vymazání souboru ze složky, vypíše se na začátku skriptu připravené hlášení o bezchybném provedení. Pokud v některé části provádění selže, nastaví se chybové hlášení, skript je dále jen vypíše a ukončí se.

Ke stahování souborů slouží samostatný skript vdownload.php (narozdíl od předchozích se nevkládá do hlavního skriptu, je v kořeni stejně jako index.php):

<?php
if (!empty($_GET[‚file‘]))
{
  require(‚handle/config.php‘);
  require(‚handle/opendb.php‘);
  $file_path=SubStr($_SERVER[‚SCRIPT_NAME‘], 0, StrRPos($_SERVER[‚SCRIPT_NAME‘],’/‘));
  $file_path.=’/‘.$store_files.$_GET[‚file‘];
  $file_path=’http://‘.$_SERVER[‚HTTP_HOST‘].$file_path;
  if (@MySQL_Query(„UPDATE „.$dbappname.“vdisk SET dloaded = dloaded+1 WHERE filename='“.$_GET[‚file‘].“‚ LIMIT 1″))
  {
    Header(„Location: $file_path“);
    Exit;
  }
  else
    echo ‚Došlo k chybě databáze!‘;
}
?>

Ověří se, je-li zadán soubor, který se má stáhnout. Pokud ano, zavede se potřebné nastavení a připojí se k databázovém serveru. Dále se získá absolutní cesta ke složce se soubory (je důležitá do hlavičky pro přesměrování), v databázi se o 1 zvýší počet stažení souborů. Pokud proběhla práce s databází v pořádku, přesměrujeme prohlížeč na získanou absolutní cestu, ke které je přidána proměnná obsahující složku se soubory a jméno souboru ke stažení. Tento skript by bylo dobré poupravit tak, aby i při chybě odesílal do prohlížeče korektní XHTML kód, což jsem pro zjednodušení vynechal.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *