views:

120

answers:

3

Hi everyone

I was wondering how I could start generating temporarily download links based on files from a protected directory (e.g. /downloads/). These links need to be valid until someone used it 5 times or so or after a week or so, after that the link shouldn't be accessible anymore.

Any help would be appreciated.

A: 

Use a database. Every time a file is downloaded the database would be updated, as soon as a certain file has reached it's limit it can be either removed or it's access could be denied. For example:

$data = $this->some_model->get_file_info($id_of_current_file);
if ( $data->max_downloads <= 5 )
{
    // Allow access to the file
}
Yorick Peterse
+1 with the addition: one might want to count bytes rather then requests, especially for large files when a user-agent tries to resume a failed/incomplete download (and it wouldn't hurt to implement resuming: http://stackoverflow.com/questions/157318/resumable-downloads-when-using-php-to-send-the-file)
Wrikken
thanks Wrikken for psoting that other question
Ayrton
Also pay special attention to the headers (I'm sure they're covered in that other question referred to) as setting these correctly tends to the make or break of dynamically generated downloads!
Kurucu
A: 

I generally keep files outside of the website directory structure for security and request like so:

    function retrive_file($file_hash)
   {
        $this->_redirect();

    $this->db->where('file_hash', $file_hash);
    $query = $this->db->get('file_uploads');

    if($query->num_rows() > 0)
 {

        $file_info = $query->row();

        if($file_info->protect == 1){
            $this->_checklogin();
        }

        $filesize = filesize($file_info->file_path . $file_info->file_name);
        $file = fopen($file_info->file_path . $file_info->file_name, "r");

        // Generate the server headers
        if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
        {
            header('Content-Type: "application/octet-stream"');
            header('Content-Disposition: attachment; filename="'.$file_info->file_name.'"');
            header('Expires: 0');
            header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
            header("Content-Transfer-Encoding: binary");
            header('Pragma: public');
            header("Content-Length: ".$filesize);
        }
        else
        {
            header('Content-Type: "application/octet-stream"');
            header('Content-Disposition: attachment; filename="'.$file_info->file_name.'"');
            header("Content-Transfer-Encoding: binary");
            header('Expires: 0');
            header('Pragma: no-cache');
            header("Content-Length: ".$filesize);
        }

        if($file)
        {
            while(!feof($file)){
                set_time_limit(0);
                echo fread($file, $filesize);
                flush();
                ob_flush();
            }
        }
        fclose($file);
    }
}

It would be pretty trivial to add byte/request counting to this.

DRL
A: 

One clever solution I've stumbled upon lately if you're using apache (or lighty) is to use mod_xsendfile (http://tn123.ath.cx/mod_xsendfile/), an apache module that uses a header to determine which file to deliver to the user.

It's very simple to install (see link above), and afterward, just include these lines in your .htaccess file:

XSendFile on
XSendFileAllowAbove on

Then in your php code, do something like this when you want the user to receive the file:

header('X-Sendfile: /var/notwebroot/files/secretfile.zip')

Apache will intercept any response with an X-Sendfile header, and instead of sending whatever content you output (you may as well return a blank page), apache will deliver the file.

This takes out all the pain of dealing with mimetypes, chunking, and miscellaneous headers.

Alan Christopher Thomas