8

In my function I'm downloading a file that saves downloads to a log file. Whenever I try to download an Excel file I uploaded, Excel states that it is corrupted. My local copy works fine. Here is my download.php:

<?php
include_once 'includes/db_connect.php';
include_once 'includes/functions.php';

sec_session_start();
ob_start();
?>

<?php if (login_check($mysqli) == true) :?>
<?php
$logFile = "download.log";
$directory = "./downloads/";
date_default_timezone_set('America/New_York');

$filename = $_GET['file'];
$path = "$directory$filename";
if(file_exists($path) AND substr_count($filename,"/") == "0") {
  if (isset($logFile)) {
    $downloadLogRecord = $filename." || ".$_SESSION['name']." || ".$_SESSION['username']." || ".$_SERVER['REMOTE_ADDR']." || ".date('Y-m-d H:i:s')."\r\n";
    @file_put_contents($logFile,$downloadLogRecord,FILE_APPEND|LOCK_EX);
  }
  header("Content-type: application/octet-stream"); 
  header("Content-Disposition: attachment; filename=$filename"); 
  header("Content-Length: ".filesize($path));
  readfile("$path");
}
?>
<?php else : ?>
  <p>
    <span class="error">You are not authorized to access this page.</span> Please <a href="index.php">login</a>.
  </p>
<?php endif; ?>

How can I fix this?

  • 2
    Your ob_start will buffer output; but unless you flush the buffer at some point, then your whitespace between `?>` and ` – Mark Baker Mar 05 '14 at 23:47

4 Answers4

16

Figured it out. I simply added ob_get_clean(); before readfile(); and ob_end_flush(); after it.

  • 1
    Thank you! Of all offered solutions this was the one that got my problem solved in which the server side script would deliver a corrupt file while deliverd from localhost my script worked fine. You created great relieve after hours of desperately searching for unnnecessary linebreaks and trying numerous stackoverflow answers! Really THANK YOU – Max Apr 01 '16 at 13:15
  • 1
    I'm late to the party, but just like @Max said - this solved my issue as well! :) – prk Sep 20 '17 at 08:55
  • This doesn't solve my problem, for my case I have a ".xltx" file. I think this extension is not accepted by http or https – NASSIM ADRAO Jan 04 '22 at 11:46
13

Keep in mind you can have nested output buffers, and this will corrupt your download as well (cheers to Vladamir for figuring this out). So to clear the output buffer completely (instead of just running ob_end_clean();) run this before you read your file:

while (ob_get_level()) {
    ob_end_clean();
}   

This will clear out your entire buffer and you won't have any extra characters to mess up your download.

Maximus
  • 1,417
  • 15
  • 19
2

I also had the same situation: In my function I was downloading a PDF file, and the file always downloads corrupted, I spent hours giggling around, nothing helped, finally:

I had to add die(); or exit(); as the last line, because I was using a MVC Framework, and MVC was causing to render view after action was finished.

Bottom line: make sure you do not have any code executed after the last line readfile(FILE_NAME)

AamirR
  • 11,672
  • 4
  • 59
  • 73
0

I had the same problem in Laravel, I tried these solutions but didn't work. I used return readfile($doc) which was causing the problem .This is my working code:

header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=" . basename($doc));
header("Content-Type: " . $file_extension);
//return readfile($doc);
readfile($doc);

I got the corrupted file error only for excel and others were working fine. But when i removed return from return readfile($doc); excel also worked fine.

RoshJ
  • 461
  • 1
  • 6
  • 24