Občas se hodí možnost nabízet ke stažení i soubory, které by jinak prohlížeč zobrazil. Tato možnost může být využita třeba u webmailových aplikací, kde chceme uživatele nechat stáhnout nebo zobrazit přílohu emailu nebo pokud chceme zajistit zabezpečené stahování souborů až po přihlášení uživatele.

Výčet různých druhů aplikací, kde se dá funkce stahování využít, je velmi velký, pravděpodobně ji však nejvíce využijí ti, kteří se chystají na vývoj již výše zmíněného webmailu nebo obrázkové galerie s možností stažení fotografií, a také ti, kteří si nepřejí dovolit uživatelům své aplikace stahovat nebo zobrazovat soubory bez přihlášení, popřípadě chtějí přiřazovat uživatelům různá práva pro čtení souborů.

Jelikož se mi nepodařilo nikde zjistit, jak v ASP.NET pomocí nějaké metody rozpoznat Content Type souboru podle jeho přípony, vytvořil jsem si exportem systémového registru XML soubor, kam jsem uložil většinu běžně na internetu používaných přípon a jejich Content Type. Pro čtení z XML souboru jsem použil postup z článku XML v ASP.NET – načítání dat v C#, kde najdete jeho detailní popis.

Samotná funkce skriptu je velmi jednoduchá. Nejdříve zjistíme, jaký soubor si uživatel vyžádal, jaká je jeho cesta na serveru a jaká je jeho přípona. V dalším kroku zjistíme pomocí XML Content Type souboru a ověříme, jestli se soubor nenachází ve vyšší adresářové úrovni než náš skript (někdo by se mohl pokoušet stáhnout například soubor „../../web.config“). Pokud je vše v pořádku, necháme soubor uživatele stáhnout. Prohlédněte si ukázku (zdrojový kód).

Nejprve si na příkladu ukážeme, jak vypadá náš XML soubor:

 <?xml version=“1.0″ standalone=“yes“ ?>
 <ContentTypes>
    <jpg>image/jpeg</jpg>
    <doc>application/msword</doc>
 </ContentTypes>

Jak můžeme rozpoznat z tohoto malého výňatku, soubor obsahuje kořenový element ContentTypes, jeho podelementy mají vždy název některé z přípon a jejich hodnota je námi požadovaný Content Type. Nyní si již ukážeme samotný programový kód skriptu:

<%@ Page Language=“C#“ Debug=“false“ EnableSessionState=“false“ %>
<%@ import Namespace=“System.IO“ %>
<%@ import Namespace=“System.Xml“ %>
<script runat=“server“>
void Page_Load(object sender, EventArgs e)
{
  string InBrowser = Request.QueryString[„InBrowser“];
  string FileName = Request.QueryString[„File“];
  string FilePath = Server.MapPath(FileName);
  string FileExt = Path.GetExtension(FilePath);
  FileInfo DownloadFileInfo = new FileInfo(FilePath);
  FileExt = FileExt.Substring(1, (FileExt.Length – 1)); // odstraneni tecky z pripony
  XmlNode xNode;
  XmlDocument xDoc = new XmlDocument();
  xDoc.Load(Server.MapPath(„ContentTypes.xml“)); // cteni z XML souboru
  string FileDirectory = DownloadFileInfo.DirectoryName;//adresar pozadoveho souboru
  string ScriptDirectory = Server.MapPath(„.“); //adresar kde se nachazi tento skript
  if (0<=FileDirectory.IndexOf(ScriptDirectory)) // zjisteni jestli uzivatel nezada
   {   // soubor z vyssi urovne
     //zacatek vypisovani hlavicek
     Response.Clear();
     Response.AddHeader („Content-Description“,FileName);
     Response.AddHeader („Content-Length“,Convert.ToString(DownloadFileInfo.Length));
     if (((xNode = xDoc.SelectSingleNode(„ContentTypes/“ + FileExt)) != null) & (InBrowser==“yes“))
      {
         // podarilo se rozpoznat Content Type souboru, otvirani v prohlizeci
         Response.ContentType = xNode.InnerText;
         Response.AddHeader („Content-Disposition“,“; filename=“ + FileName);
       }
     else
      {
         // neznamy Content Type – bude se stahovat
         Response.ContentType = „application/octet-stream“;
         Response.AddHeader („Content-Disposition“,“attachment; filename=\““ + FileName + „\““);
       }
     Response.WriteFile(FilePath);
     Response.End(); // konec odpovedi do prohlizece
  }
}
</script>

Ze zdrojového kódu se dá poměrně snadno vyčíst, jak se bude náš skript volat. Má dva parametry. První z nich je parametr File, kterým skriptu sdělujeme jméno stahovaného souboru (přičemž soubor je možno stáhnout pouze z adresáře, kde je umístěn náš skript nebo z některých z jeho podadresářů). Druhý parametr InBrowser slouží pro určení, zda chceme zkusit otevřít soubor v prohlížeči či nikoli. Pokud náš skript pozná soubor podle jeho přípony (tedy najde odpovídající záznam v XML souboru) a zároveň skript zavoláme s parametrem InBrowser=yes, otevře jej v prohlížeči. Pakliže záznam nenajde nebo parametr InBrowser vynecháme, nabídne nám soubor ke stažení.

Nyní si rozebereme jednotlivé části skriptu. První část skriptu slouží výhradně k zjištění informací a zavedení proměnných, které budeme dále pro zpracování požadavku na stažení potřebovat. Nejprve tedy přijmeme předané parametry a uložíme je do proměnných FileName a InBrowser, se kterými budeme dále pracovat. Dále zjistíme absolutní cestu k souboru na serveru – k tomuto nám poslouží Server.MapPath() – a vytvoříme instanci třídy FileInfo, kterou budeme potřebovat pro zjištění velikosti souboru a jeho absolutní cesty na serveru. A jelikož metoda Path.GetExtension, pomocí které zjišťujeme příponu stahovaného souboru, vrací příponu s tečkou na začátku, zbavíme se jí vhodným použitím metody SubString.

V druhé části skriptu přistoupíme k samotnému stahování. Ze všeho nejdříve ale musíme zjistit jména adresářů, ve kterých se nachází náš skript a požadovaný soubor. U stahovaného souboru to provedeme přes vlastnost DirectoryName vytvořené instance třídy FileInfo, u skriptu použijeme Server.MapPath(). Následně ověříme, jestli cesta k adresáři stahovaného souboru obsahuje cestu k adresáři našeho skriptu (tedy jestli se stahovaný soubor nachází ve stejném adresáři jako skript, případně jestli se nachází v jeho podadresářích). Jestliže je podmínka pro umístění souboru splněna, vymažeme jakoukoli dosud odeslanou odpověď do bufferu a zapíšeme několik důležitých hlaviček pomocí metody Response.AddHeader(). První z nich sděluje prohlížeči jméno souboru, pod kterým ho má uložit, druhá předává jeho délku.

Nyní je na řadě zjišťování, jestli se jedná o známý typ souboru a zda si přeje uživatel soubor zobrazit nebo stáhnout. Zkusíme tedy v XML souboru najít řádek s odpovídající příponou a přečíst příslušný Content Type. Pokud je soubor známého typu (podařilo se najít řádek v XML) a uživatel si přeje soubor zobrazit, odešle se hlavička, která nevynutí na prohlížeči stahovací dialog, nýbrž jen pošle soubor – prohlížeč ho zobrazí (například obrázek). Odešleme rovněž prohlížeči informaci o Content Type. Pokud je soubor neznámého typu nebo si jej uživatel nepřeje zobrazit, pošleme prohlížeči hlavičku, která stahovací dialog vynutí a Content Type nastavíme na univerzální application/octet-stream. Tím jsme již skoro u konce, zbývá již jen zapsat samotný soubor pomocí Response.WriteFile() a ukončit zápis výstupu do prohlížeče.

Kvůli větší přehlednosti jsem do skriptu nezařadil ověření, zda zadaný soubor existuje, což ale velmi důrazně doporučuji provést, pokud chcete tento skript používat jinak než pro svou vlastní potřebu. Rovněž jsem neošetřoval ve skriptu žádné výjimky, opět v rámci co největší možné přehlednosti kódu. Doporučuji vám přečíst si článek o chybových hlášeních v ASP.NET a navštívit na MSDN sekci věnovanou řízení výjimek.

Popisovaný skript bez problémů zpracuje Microsoft Internet Explorer i Mozilla, problémy jsou s Operou, neumožňuje totiž stáhnout soubory, které umí zobrazit (obrázky, textové soubory a podobně).

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

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

Žádný příspěvek v diskuzi

Odpovědět