codeignitercodeigniter-2x-sendfile

xsendfile only works from index


I'm trying to send a file to the user using xsendfile within the code igniter framework.

It is all installed correctly, my problem is that it only seems to work from the route, even though every page comes from index.php anyway.

This is my function:

function _output_file($name, $root_location, $public_location = FALSE)
{
    if (function_exists('apache_get_modules') && in_array('mod_xsendfile', apache_get_modules())) {
        header ('Content-Description: File Transfer');
        header ('Content-Type: application/octet-stream');
        if (strstr($_SERVER["HTTP_USER_AGENT"], "MSIE") != FALSE) {
            header ('Content-Disposition: attachment; filename='.urlencode($name));
        } else {
            header ('Content-Disposition: attachment; filename="'.$name.'"');
        }
        //86400 is one day
        header ('Expires: '.gmdate('D, d M Y H:i:s', (TIME_NOW + 86400)));
        header ('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header ('Pragma: public');
        header ('X-Sendfile: '.$root_location);
        exit;
    } else {
        redirect(site_url($public_location));
    }
}

If I place this at the top of my index.php and load the root it works fine, but if I try to access it from domain.com/controller/function it returns a 404 error.

It is definitely using the index.php file since if I replace the function call with die("test"); this displays to the screen.

I believe it's something to do with what permissions xsendfile has to access the file, but since it's working from the root index.php I would have thought it would have complete permissions, presumably it's based on what the request url is, which I find strange.

So.... does anyone have any suggestions as to how I can get xsendfile to work through codeigniter, from a url such as "domain.com/files/get/12"?


Solution

  • It seems this answer never got a reponse, in the end I just created a file in my root called "getfile.php", it's not perfect but it gets the job done for now, here it is for anyone that may find it useful.

    <?php
    define('BASEPATH', 'just done to stop direct access being disallowed');
    
    function show_getfile_error()
    {
        echo 'You do not have permission to download this file, if you think this is a mistake please get in contact.';
        exit;
    }
    
    include('applications/config/database.php');
    $mysqli = new mysqli($db['default']['hostname'], $db['default']['username'], $db['default']['password'], $db['default']['database']);
    if(!preg_match('%^[0-9]+$%', $_GET['key']))
    {
        show_getfile_error();
    }
    else
    {
        $query = mysqli_query($mysqli, 'SELECT * FROM getfiles WHERE getfile_key = '.(int)$_GET['key']);
    
        $result = mysqli_fetch_array($query, MYSQLI_ASSOC);
    
        if(!$result || $result['getfile_ip'] != $_SERVER['REMOTE_ADDR'])
        {
            show_getfile_error();
        }
    
        header ('Content-Description: File Transfer');
        header ('Content-Type: application/octet-stream');
        if (strstr($_SERVER["HTTP_USER_AGENT"], "MSIE") != FALSE) {
            header ('Content-Disposition: attachment; filename='.urlencode($result['getfile_name']));
        } else {
            header ('Content-Disposition: attachment; filename="'.$result['getfile_name'].'"');
        }
        //86400 is one day
        header ('Expires: '.gmdate('D, d M Y H:i:s', (TIME_NOW + 86400)));
        header ('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header ('Pragma: public');
        header ('X-Sendfile: '.$result['getfile_location']);
    }
    ?>