views:

46

answers:

2

Hi

I want to know how to implement logic for my download button in PHP. I have a file in web server and have a page with download button. I want to start download a file when user press download button. How to implenent? Thanks

+1  A: 

Here is how you start a download without letting the user see the real path of the file. Make your link point to download.php?file=filename, and be sure the file exists in the download folder. Then use this code to check if the file exists and let them download it. Optionally, you can put a login check or some other check in it.

<?php
//download.php
$dir = '/path/to/file/'; 
if (isset($_GET['file']) && !is_dir($_GET['file']) && file_exists($dir . $_GET['file'] . '.zip')) 
{ 
    $file = $dir . $_GET['file'] . '.zip'; 
    header('Content-type: application/force-download'); 
    header('Content-Transfer-Encoding: Binary'); 
    header('Content-length: ' . filesize($file)); 
    header('Content-disposition: attachment; filename=' . basename($file)); 
    readfile($file); 
} 
else 
{ 
    echo 'No file selected'; 
} 
?>

In addition, you can also block access to the folder with the files in it with a .htaccess file. If you want to, put the following code in a .htaccess file in the files dir.

order allow, deny
deny from all
Evert
This is pretty dangerous. What if I decide to request the file `../../../home/user/something`? `file_exists` will match the request, assuming the file does actually exist, and `readfile` will happily read the file `/path/to/file/../../../home/user/something`. You *can* secure this kind of method, but why take that risk?
Welbog
@Welbog: thanks! I totally forgot about that.. I've changed the script so that it only allows downloading a .zip file. It could be extended of course, so that it allows more extensions like .pdf or something.
Evert
That's a little bit better, but it still lets you grab zip files from other directories. It would be best to not use user-supplied identifiers in real paths whatsoever.
Welbog
@Welbog: I've put in is_dir(). Now you can only specify a file name and not hack it by using a directory or ../. Thanks for your criticism, it made this script more hack-proof. :-)
Evert
+1  A: 

In both provided sollutions (readfile or X-Sendfile header) the file can be stored outside the public server directory (usually called htdocs or www).

//page.php
<form method="get" action="download.php">
    With button <input type="submit" value="Download file" />
</form>
or
With link <a href="download.php">Download file</a>


<?php // download.php
$file = '/path/to/file.zip';

if (file_exists($file)) {
    // send headers that indicate file download
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename='.basename($file));
    header('Content-Transfer-Encoding: binary');
    header('Content-Length: ' . filesize($file));
    ob_clean();
    flush();
    // send file (must read comments): http://php.net/manual/en/function.readfile.php
    readfile($file);
    exit;
}

A better sollution, if your server supports the X-Sendfile (mod_xsendfile) header is:

<?php
header('Content-Disposition: attachment;filename=hello.txt');
header('X-Sendfile: /path/to/file.zip');

http://codeutopia.net/blog/2009/03/06/sending-files-better-apache-mod_xsendfile-and-php/

clyfe