sabato 18 agosto 2007

Php: gestire i download

Vediamo oggi come scrivere poche righe di php per forzare lo scaricamento di un file.
Spesso infatti si ha la necessità di mettere files sul proprio webserver non direttamente scaricabili (ad esempio, i vari server per l'hosting gratuito di file come http://www.box.net). In questo caso non si usano link diretti ai file scaricabili, ma pagine che permettono di scaricare files contenuti in directory nascoste e non accessibili dall'esterno.
Attenzione però alla sicurezza: un downloader non deve fare scaricare files sensibili!Quindi non create obbrobri di questo tipo: download.php?file=Documento.pdf e magari $filename=$_REQUEST["file"] (vedi codice sotto per intenderci). Una richiesta malevola (download.php?file=../../../../../../etc/passwd) può combinare dei bei casini...Meglio appoggiarsi ad un database con corrispondenze stringaID->nomefile; ad esempio, la stringa "ad6cjf6d5s" fa riferimento a "Documento.pdf". La richiesta allo script (che quindi dovrà leggere dal DB) sarà download.php?id=ad6cjf6d5s.
Mostro adesso un semplice script, che non ha le caratteristiche di lettura dal DB appena citate, ma può servire come base per una successiva modifica :-)

<?
$filename
="Documento.pdf";
header ("Pragma: public");
header ("Expires: 0");
header ("Cache-Control: must-revalidate, post-check=0, pre-check=0");

header ("Cache-Control: private", false);
header ("Content-Type: application/force-download");
header ("Content-Disposition: attachment; filename=\"" . $filename . "\";");

header ("Content-Description: File Transfer");
header ("Content-Transfer-Encoding: binary");
header ("Content-Length: " . filesize ($filename));

set_time_limit (0);
@
readfile ($filename);
?>


Come potete vedere, il file da scaricare è "Documento.pdf", ma può essere automatizzato (con tutti gli accorgimenti di sicurezza del caso, come ho descritto in precedenza). Vengono inviati i vari header al browser dopodichè viene inviato tutto il contenuto del file (readfile), con particolare attenzione ad eludere il time limit del php (il file da scaricare può anche essere parecchi mega, e portare via parecchio tempo).

Buon php a tutti!

2 commenti:

Anonimo ha detto...

Ciao. Ho letto il tuo post perchè è proprio quello che mi serve. Ti volevo chiedere come implementare la sicurezza del download del file passando un id piuttosto che il nome del file effettivo. Vorrei chiederti proprio come agire sul db e come modificare il file php.

Ti ringrazio,
Antonella.

Unknown ha detto...

Ciao, io opterei per una tabella su MySql con i campi id e percorso file, qualcosa di questo genere:
CREATE TABLE `prova`.`download` (
`id` INT NOT NULL AUTO_INCREMENT ,
`percorsofile` VARCHAR( 255 ) NOT NULL ,
PRIMARY KEY ( `id` )
)
Questa tabella conterrà i percorsi nascosti dei file da scaricare.
L'idea è quella di dare in pasto allo script, via GET o POST, un campo ID da cui estrarre "di nascosto" il vero percorso del file da scaricare.
A questo punto non rimane che fare una piccola modifica nello script del post: modificare la riga
$filename="Documento.pdf";
con:
$result=mysql_query("SELECT * FROM download WHERE id='".mysql_escape_string($_REQUEST["id"])."' ");
$filename=mysql_result($result, 0, "percorsofile");

E il gioco è fatto :-)
Ho tralasciato tutti i comandi di connessione al DBMS per semplicità.