Imvikky
Imvikky

Reputation: 193

PHP readfile() causing corrupt file downloads

I am using php script to provide download from my website after a requisite javascript timer this php script is included which causes the download. But the downloaded file is corrupt no matter whatever I try. Can anyone help me point out where am I going wrong.

This is my code

     <?php
include "db.php";    
 $id = htmlspecialchars($_GET['id']);
 $error = false;
    $conn = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD);
    if(!($conn)) echo "Failed To Connect To The Database!";
    else{   
        if(mysql_select_db(DB_NAME,$conn)){
            $qry = "SELECT Link FROM downloads WHERE ID=$id";
            try{
                $result = mysql_query($qry);
                if(mysql_num_rows($result)==1){
                    while($rows = mysql_fetch_array($result)){
                        $f=$rows['Link'];
                    }
                    //pathinfo returns an array of information
                    $path = pathinfo($f);
                    //basename say the filename+extension
                    $n = $path['basename'];
                    //NOW comes the action, this statement would say that WHATEVER output given by the script is given in form of an octet-stream, or else to make it easy an application or downloadable
                    header('Content-type: application/octet-stream');
                    header('Content-Length: ' . filesize($f));
                    //This would be the one to rename the file
                    header('Content-Disposition: attachment; filename='.$n.'');
                    //Finally it reads the file and prepare the output
                    readfile($f);
                    exit();
                }else $error = true;
            }catch(Exception $e){
                $error = true;
            }
            if($error) 
            {
                header("Status: 404 Not Found");
                }
        }
  }
?> 

Upvotes: 16

Views: 16181

Answers (3)

GeniusGeek
GeniusGeek

Reputation: 335

           ob_start();//add this to the beginning of your code 

      if (file_exists($filepath) && is_readable($filepath) ) {
header('Content-Description: File Transfer');
 header("Content-Type: application/octet-stream");
 header("Content-Disposition: attachment; filename=$files");
 header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
   header('Pragma: public');
 header("content-length=".filesize($filepath));
 header("Content-Transfer-Encoding: binary");

   /*add while (ob_get_level()) {
       ob_end_clean();
         }  before readfile()*/
  while (ob_get_level()) {
ob_end_clean();
   }
    flush();
   readfile($filepath);

Upvotes: 5

Pedro Cordeiro
Pedro Cordeiro

Reputation: 2125

First of all, as some people pointed out on the comments, remove all spaces before the opening PHP tag (<?php) on the first line and that should do the trick (unless this file is included or required by some other file).

When you print anything on the screen, even a single space, your server will send the headers along with the content to be printed (in the case, your blank spaces). To prevent this from happening, you can:

a) not print anything before you're done writing the headers;

b) run an ob_start() as the first thing in your script, write stuff, edit your headers and then ob_flush() and ob_clean() whenever you want your content to be sent to the user's browser.

In b), even if you successfully write your headers without getting an error, the spaces will corrupt your binary file. You should only be writing your binary content, not a few spaces with the binary content.

The ob_ prefix stands for Output Buffer. When calling ob_start(), you tell your application that everything you output (echo, printf, etc) should be held in memory until you explicitly tell it to 'go' (ob_flush()) to the client. That way, you hold the output along with the headers, and when you are done writing them, they will be sent just fine along with the content.

Upvotes: 15

Janos Szabo
Janos Szabo

Reputation: 1352

This helped me in case of more output buffers was opened.

//NOW comes the action, this statement would say that WHATEVER output given by the script is given in form of an octet-stream, or else to make it easy an application or downloadable
header('Content-type: application/octet-stream');
header('Content-Length: ' . filesize($f));
//This would be the one to rename the file
header('Content-Disposition: attachment; filename='.$n.'');
//clean all levels of output buffering
while (ob_get_level()) {
    ob_end_clean();
}
readfile($f);
exit();

Upvotes: 29

Related Questions