Asked  7 Months ago    Answers:  5   Viewed   32 times

Using PHP, I am trying to serve large files (up to possibly 200MB) which aren't in a web accessible directory due to authorization issues. Currently, I use a readfile() call along with some headers to serve the file, but it seems that PHP is loading it into memory before sending it. I intend to deploy on a shared hosting server, which won't allow me to use much memory or add my own Apache modules such as X-Sendfile.

I can't let my files be in a web accessible directory for security reasons. Does anybody know a method that is less memory intensive which I could deploy on a shared hosting server?

EDIT:

if(/* My authorization here */) {
        $path = "/uploads/";
        $name = $row[0];           //This is a MySQL reference with the filename
        $fullname = $path . $name; //Create filename
        $fd = fopen($fullname, "rb");
        if ($fd) {
            $fsize = filesize($fullname);
            $path_parts = pathinfo($fullname);
            $ext = strtolower($path_parts["extension"]);
            switch ($ext) {
                case "pdf":
                header("Content-type: application/pdf");
                break;
                case "zip":
                header("Content-type: application/zip");
                break;
                default:
                header("Content-type: application/octet-stream");
                break;
            }
            header("Content-Disposition: attachment; filename="".$path_parts["basename"].""");
            header("Content-length: $fsize");
            header("Cache-control: private"); //use this to open files directly
            while(!feof($fd)) {
                $buffer = fread($fd, 1*(1024*1024));
                echo $buffer;
                ob_flush();
                flush();    //These two flush commands seem to have helped with performance
            }
        }
        else {
            echo "Error opening file";
        }
        fclose($fd);

 Answers

97

If you use fopen and fread instead of readfile, that should solve your problem.

There's a solution in the PHP's readfile documentation showing how to use fread to do what you want.

Wednesday, March 31, 2021
 
Sanguine
answered 7 Months ago
73

Change you're header from :

header("Content-type:application/octet-stream");

To :

header("Content-type: video/flv");

Then you can do :

header("Content-Disposition:attachment;filename="$psp"");
//allways a good idea to let the browser know how much data to expect
header("Content-length: " . filesize($psp) . "nn"); 
echo file_get_contents($psp); //$psp should contain the full path to the video
Wednesday, March 31, 2021
 
EurekA
answered 7 Months ago
39

This is gotten from the manual, and is for windows (Since you didn't specify the OS.) using the COM class.

Note : This has nothing to do with the client side.

<?php
 $fso = new COM('Scripting.FileSystemObject'); 
    $D = $fso->Drives; 
    $type = array("Unknown","Removable","Fixed","Network","CD-ROM","RAM Disk"); 
    foreach($D as $d ){ 
       $dO = $fso->GetDrive($d); 
       $s = ""; 
       if($dO->DriveType == 3){ 
           $n = $dO->Sharename; 
       }else if($dO->IsReady){ 
           $n = $dO->VolumeName; 
           $s = file_size($dO->FreeSpace) . " free of: " . file_size($dO->TotalSize); 
       }else{ 
           $n = "[Drive not ready]"; 
       } 
   echo "Drive " . $dO->DriveLetter . ": - " . $type[$dO->DriveType] . " - " . $n . " - " . $s . "<br>"; 

    } 

      function file_size($size) 
      { 
      $filesizename = array(" Bytes", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"); 
      return $size ? round($size/pow(1024, ($i = floor(log($size, 1024)))), 2) . $filesizename[$i] : '0 Bytes'; 
      } 


?> 

Would output something similar to

Drive C: - Fixed - Bla - 88.38 GB free of: 444.14 GB
Drive D: - Fixed - Blas - 3.11 GB free of: 21.33 GB
Drive E: - Fixed - HP_TOOLS - 90.1 MB free of: 99.02 MB
Drive F: - CD-ROM - [Drive not ready] - 
Drive G: - CD-ROM - Usb - 0 Bytes free of: 24.75 MB
Drive H: - Removable - [Drive not ready] - 
Saturday, May 29, 2021
 
Uours
answered 5 Months ago
85
<?php
$file = 'monkey.gif';

if (file_exists($file)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename='.basename($file));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));
    ob_clean();
    flush();
    readfile($file);
    exit;
}

?>

As you can see Content type is application/octet-steam meaning file is byte by byte encoded. Also the cache headers are set. Then headers are forcefully sent by ob_clean();flush(); and then the file is read.

The file_exists is there to ensure that given file exists. You should also try not not thrust user input as they could easy write names for your php codes and download EACH file. And with ../ in names of the files, even your documents or system files and so on.

Saturday, May 29, 2021
 
Extrakun
answered 5 Months ago
13

The first thing you should do is get a tool like Fiddler and visit a YouTube video page. In Fiddler, you will see all of the files that make up that page, including the FLV itself. Now, you know that the video isn't one of the CSS files, nor is it the image files. You can ignore those. Look for a big file. If you look at the URL, it begins with /videoplayback.

Now, once you've found it, figure out how the browser knew to get that file. Do a search through the sessions (Ctrl+F) and look for "videoplayback". You will see a hit on the first page you went to, like http://www.youtube.com/watch?v=123asdf. If you dig through that file, you'll see a DIV tag with the ID of "watch-player". Within that there is a script tag to setup the flash player, and within that are all of the flash parameters. Within those is the URL to the video.

So now you know how to use your tools to figure out how the browser got to it. How do you duplicate this behavior in PHP?

Do a file_get_contents() on the page that references the video. Ignore everything not in that watch-player div. Parse through the code until you find that variable that contains the URL. From there you will probably have to unescape that URL. Once you have it, you can do a file_get_contents() (or some other download method, depending on what you are trying to do) to get the URL. it is that simple. Your HTML parsing code will be the most complex.

Finally, keep in mind what you are about to do may be illegal. Check the EULA.

Sunday, August 8, 2021
 
im1dermike
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :