Search code examples
phpproxydownload-managersource-code-protection

PHP proxy to download files


I think this is a easy one.

I have a simple website with a reserved area and some files to download from it.

I disabled direct file download with .htaccess, and I manage the download of file via this simple proxy.php file:

// ....

if ($filename === null || !file_exists($proxiedDirectory.$filename)) {
    http_response_code(404);
    exit;
}

if (!$site->is_logged_in()) { 
    http_response_code(403);
    exit;
}

$fp = fopen($proxiedDirectory.$filename, 'rb');

header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filename));
fpassthru($fp);
exit;

This code WORKS perfectly, but unfortunately I have a 1.2Gb file that users could download, and this script is too slow, and doesn't allow the full file download.

Any help would be appreciated,

Thanks in advance!

M.


Solution

  • You can use a combination of header(), set_time_limit(), fgets(), ob_flush(), flush(). Here is my example, best using Php 64bit on OS 64bit because filesize() has not limits in this architecture.

        // Set force download headers
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . $file . '"');
        header('Content-Transfer-Encoding: binary');
        header('Connection: Keep-Alive');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        header('Content-Length: ' . sprintf("%u", filesize($downloads_folder . $file)));
        // Open and output file contents
        set_time_limit(0);
        $fh = fopen($downloads_folder . $file, "rb");
        while (!feof($fh)) {
          echo fgets($fh);
          ob_flush();
          flush();
        }
        fclose($fh);
        exit;
    

    Hope this helps.