PHP Force Download - univerzální stažení souboru pomocí header

Zřejmě každý začínající PHP vývojář se jednou setká s problémem jak vytvořit možnost stažení souboru aniž by uživatel měl možnost vidět přesnou adresu odkud soubor stahuje. Toto a spoustu dalších výhod nabízí jednoduchá funkce, kterou naleznete v článku.

Zdrojový kód funkce force download

<?php
function force_download ($data, $name, $mimetype='', $filesize=false) {
    // File size not set?
    if ($filesize == false OR !is_numeric($filesize)) {
        $filesize = strlen($data);
    }

    // Mimetype not set?
    if (empty($mimetype)) {
        $mimetype = 'application/octet-stream';
    }

    // Ujistíme se, že v souboru nejsou data navíc
    ob_clean_all();

    // Odeslání hlavičky
    header("Pragma: public"); // požadováno
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: private",false); // požadováno u některých prohlížečů
    header("Content-Transfer-Encoding: binary");
    header("Content-Type: " . $mimetype);
    header("Content-Length: " . $filesize);
    header("Content-Disposition: attachment; filename="" . $name . "";" );

    // Finální odeslání souboru
    echo $data;
    die();
}

function ob_clean_all () {
    $ob_active = ob_get_length () !== false;
    while($ob_active) {
        ob_end_clean();
        $ob_active = ob_get_length () !== false;
    }

    return true;
}
?>

Použití

Pro ty z vás co nejste zvyklí na použití vlastních definovaných funkcí, napíšu jednoduchý návod jak na to.

  1. Výše uvedený zdrojový kód si vložte do nějakého php souboru. Třeba download.php
  2. Tímto jste si nadefinovali funkci force_download(); kterou si následně zavoláme.
  3. force_download($data, $name, $mimetype);

Mimetypes a jejich koncovky

'hqx'   =>      'application/mac-binhex40',
'cpt'   =>      'application/mac-compactpro',
'csv'   =>      array('text/x-comma-separated-values', 'application/vnd.ms-excel'),
'bin'   =>      'application/macbinary',
'dms'   =>      'application/octet-stream',
'lha'   =>      'application/octet-stream',
'lzh'   =>      'application/octet-stream',
'exe'   =>      'application/octet-stream',
'class' =>      'application/octet-stream',
'psd'   =>      'application/x-photoshop',
'so'    =>      'application/octet-stream',
'sea'   =>      'application/octet-stream',
'dll'   =>      'application/octet-stream',
'oda'   =>      'application/oda',
'pdf'   =>      array('application/pdf', 'application/x-download'),
'ai'    =>      'application/postscript',
'eps'   =>      'application/postscript',
'ps'    =>      'application/postscript',
'smi'   =>      'application/smil',
'smil'  =>      'application/smil',
'mif'   =>      'application/vnd.mif',
'xls'   =>      array('application/excel', 'application/vnd.ms-excel'),
'ppt'   =>      'application/powerpoint',
'wbxml' =>      'application/wbxml',
'wmlc'  =>      'application/wmlc',
'dcr'   =>      'application/x-director',
'dir'   =>      'application/x-director',
'dxr'   =>      'application/x-director',
'dvi'   =>      'application/x-dvi',
'gtar'  =>      'application/x-gtar',
'gz'    =>      'application/x-gzip',
'php'   =>      'application/x-httpd-php',
'php4'  =>      'application/x-httpd-php',
'php3'  =>      'application/x-httpd-php',
'phtml' =>      'application/x-httpd-php',
'phps'  =>      'application/x-httpd-php-source',
'js'    =>      'application/x-javascript',
'swf'   =>      'application/x-shockwave-flash',
'sit'   =>      'application/x-stuffit',
'tar'   =>      'application/x-tar',
'tgz'   =>      'application/x-tar',
'xhtml' =>      'application/xhtml+xml',
'xht'   =>      'application/xhtml+xml',
'zip'   =>      array('application/x-zip', 'application/zip', 'application/x-zip-compressed'),
'mid'   =>      'audio/midi',
'midi'  =>      'audio/midi',
'mpga'  =>      'audio/mpeg',
'mp2'   =>      'audio/mpeg',
'mp3'   =>      'audio/mpeg',
'aif'   =>      'audio/x-aiff',
'aiff'  =>      'audio/x-aiff',
'aifc'  =>      'audio/x-aiff',
'ram'   =>      'audio/x-pn-realaudio',
'rm'    =>      'audio/x-pn-realaudio',
'rpm'   =>      'audio/x-pn-realaudio-plugin',
'ra'    =>      'audio/x-realaudio',
'rv'    =>      'video/vnd.rn-realvideo',
'wav'   =>      'audio/x-wav',
'bmp'   =>      'image/bmp',
'gif'   =>      'image/gif',
'jpeg'  =>      array('image/jpeg', 'image/pjpeg'),
'jpg'   =>      array('image/jpeg', 'image/pjpeg'),
'jpe'   =>      array('image/jpeg', 'image/pjpeg'),
'png'   =>      array('image/png',  'image/x-png'),
'tiff'  =>      'image/tiff',
'tif'   =>      'image/tiff',
'css'   =>      'text/css',
'html'  =>      'text/html',
'htm'   =>      'text/html',
'shtml' =>      'text/html',
'txt'   =>      'text/plain',
'text'  =>      'text/plain',
'log'   =>      array('text/plain', 'text/x-log'),
'rtx'   =>      'text/richtext',
'rtf'   =>      'text/rtf',
'xml'   =>      'text/xml',
'xsl'   =>      'text/xml',
'mpeg'  =>      'video/mpeg',
'mpg'   =>      'video/mpeg',
'mpe'   =>      'video/mpeg',
'qt'    =>      'video/quicktime',
'mov'   =>      'video/quicktime',
'avi'   =>      'video/x-msvideo',
'movie' =>      'video/x-sgi-movie',
'doc'   =>      'application/msword',
'word'  =>      array('application/msword', 'application/octet-stream'),
'xl'    =>      'application/excel',
'eml'   =>      'message/rfc822'

Proč řešit download právě touto cestou?

Uvedu jednoduchý příklad. Máte webový projekt, kde nabízíte soubory komisním prodejem. Uživatel si zaplatí za jeden soubor a vy mu poskytnete url na které si může požadovaný soubor stáhnout. Tato url může vypadat následovně:

Prostý uživatel si soubor stáhne a je spokojen. Ovšem zaběhlejšímu uživatelovi už ale může dojit, že v tom samém adresáři budou zřejmě uložené i ostatní soubory a začne tipovat názvy ostatních. A profík už si jednoduše zjistí podle čeho je konkrétní název odvozen a už stahuje celkem na jisto. Samozřejmě lze název zakódovat do md5 atd., ale to už mi nepřijde jako zrovna „elegantní“ řešení.

Tímto způsobem nedáte uživateli žádnou možnost aby zjistil odkud soubor pochází. A pokud jste ještě více paranoidní, tak můžete místo id použít třeba již výše zmíněný zakódovaný string. Toto řešení mi přijde nejlepší jak z bezpečnostního hlediska, tak i z hlediska usnadnění práce a to se přece taky počítá ne? :)

hibi | 03:50, 29.3.2008 | Internet | 4 komentáře

Komentáře k textu

frant

http://stranka.cz/download.php?sou­bor={id_souboru}

uziv pak netypuje nazvy souboru, ale id.

Je treba dodat, ze se pak musi kontrolovat, zda ma uziv k tomuto id pravo (muze ho stahovat)

16:27, 10.6.2008
hibi

Je to pojaté univerzálně a tímto stylem lze opravdu ochranu před nevyžádaným stážením ošetřit mnoha způsoby. Jedním z nich je třeba ten co zmiňujete…

16:56, 10.6.2008
ii
8-O
15:22, 2.4.2010
skr

header(„Content-Type: " . $mimetype); header(“Content-Length: " . $filesize); header(„Content-Disposition: attachment; filename="" . $name . "";“ );

opravit

18:41, 29.4.2011

Kliknutím vložíš: Vlož smajla :-) Vlož smajla :-( Vlož smajla ;-) Vlož smajla :-D Vlož smajla 8-O Vlož smajla 8-) Vlož smajla :-? Vlož smajla :-x Vlož smajla :-P Vlož smajla :-|

Rubriky

Hardware Software Společnost Internet Zábava

Tagy a Štítky

php stahnout soubor header application/octet-stream application/force octet stream php force download octet-stream download souboru php
TOPlist