1

I'm using readfile() and header() for make the user download a file. This works good but today when I tried the script with a mp4 video file, the video get corrupted.

The video was uploaded with success for sure, because if I access the video directly in the address bar I can download it, but if I use my script (example download.php?id=105) I got a corrupted video. What I can't understand is because all other files (videos, images, pdf, ecc) were all downloaded correctly and this file is corrupted instead.

P.S The script I used (I repeat, it works for all file except this new video) is:

header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Type: '.$mime);
header("Content-Transfer-Encoding: Binary");
header('Content-length: '.filesize($href));
header('Content-Disposition: inline; filename="'.(pathinfo($href, PATHINFO_BASENAME)).'"');

readfile($href);

EDIT: If i remove the headers and readfile, no error is shown.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Mauro Valvano
  • 903
  • 1
  • 10
  • 21
  • Where is `readfile` in the code? – sectus Apr 04 '14 at 08:33
  • I'm sorry i have tried switching to file_get_contents but nothing changed (i corrected the post with the code with readfile) – Mauro Valvano Apr 04 '14 at 08:34
  • Ok, did you explore your error.log file? – sectus Apr 04 '14 at 08:34
  • Where is it? I'm using LAMP server on Ubuntu 12.04 – Mauro Valvano Apr 04 '14 at 08:35
  • how big is the file? filesize() sometimes has problems with files larger than 2GB on 32 bit systems – gries Apr 04 '14 at 08:36
  • is only 270kb (is a video with a image and a audio for 14 seconds) – Mauro Valvano Apr 04 '14 at 08:39
  • The error can't be anything but wrong file path (plus maybe wrong mime-type, depending on how you fetch it). – Daniel W. Apr 04 '14 at 08:43
  • the mime/type result to be video/mp4 so it is correct. – Mauro Valvano Apr 04 '14 at 08:47
  • 1) The file might actually be corrupt. 2) Do you get ANY file or no data at all? 3) You don't accidentally display any newlines at the bottom or anything do you? If it's not too big, open a downloaded copy in notepad or something and check out the bottom lines. – Arnout Apr 04 '14 at 10:22
  • did you ever solve this? – Fight Fire With Fire Jun 18 '17 at 23:00
  • @FightFireWithFire Sorry but i don't remember... on php docs you can find some example with the flush code – Mauro Valvano Jun 19 '17 at 05:01
  • Fast forward 5 years later with PHP 7.3+ having same issue, smaller files seems to be OK, but some larger ones fail consistently. Not really sure why, no errors in logs. Direct anchor links seems to be only solution, but users must right-click or hold to open dialog and select download, as otherwise file will be streamed in e.g. Chrome automatically. Or use javascript to prevent it. Seems that header of files are changed, which corrupts mp4 files and playback. I found 5 bytes difference between downloaded and original versions. – dev101 May 06 '19 at 17:36
  • Alternative to javascript is to simply use 'download' attribute in a href, e.g. https://stackoverflow.com/questions/3802510/force-to-open-save-as-popup-open-at-text-link-click-for-pdf-in-html – dev101 May 06 '19 at 17:52

5 Answers5

1

Your problem is here:

echo readfile($href);

readfile() will return integer value (number of bytes that were read). This function will output result by itself - you don't need to do echo (and you shouldn't, in fact), use:

readfile($href);

-because otherwise you'll definitely get not the thing that you're expecting

Alma Do
  • 37,009
  • 9
  • 76
  • 105
0

May be change max_execution_time & max_input_time in php.ini (php config system)

; Maximum execution time of each script, in seconds
; http://php.net/max-execution-time
; Note: This directive is hardcoded to 0 for the CLI SAPI
max_execution_time = 300

; Maximum amount of time each script may spend parsing request data. It's a good
; idea to limit this time on productions servers in order to eliminate unexpectedly
; long running scripts.
; Note: This directive is hardcoded to -1 for the CLI SAPI
; Default Value: -1 (Unlimited)
; Development Value: 60 (60 seconds)
; Production Value: 60 (60 seconds)
; http://php.net/max-input-time
max_input_time = 300
Bang Andre
  • 471
  • 7
  • 11
0

My guess is wrong file path, but you said it is correct.

Only 1 possible error source left! Where? In code above the headers, which you did not show to us.

EDIT: If I remove the headers and readfile, no error is shown.

Please, remove the headers again (not the readfile part tho), and enable error reporting:

<?php 
// Put these lines to the very top of your script
error_reporting(E_ALL);
ini_set('display_errors', true);
ini_set('display_startup_errors', true);
ini_set('xmlrpc_errors', true);

Quite sure you will see an error now.

Daniel W.
  • 31,164
  • 13
  • 93
  • 151
0

I have same problem. The downloaded file corrupts, but the one on the server is correct. This sort of happens randomly. Sometimes, the downloaded file takes the name of the script plus its .php extension. I guess it's a problem with windows. Try uploading to a linux server and see if problem persists

Ekene
  • 51
  • 10
  • @FightFireWithFire No I never solved it. I dropped the idea of using readfile() and gave the users direct download link instead, after which I rename the file. – Ekene Jun 19 '17 at 12:55
  • 1
    thank you - what worked for me was I had to move the header write to the top of the code and make sure there was no output at all. If the browser renders even one thing before the download is initiated by PHP using header, the download will be corrupt. – Fight Fire With Fire Jun 20 '17 at 16:29
  • I think I did that too @FightFireWithFire but same file would download correctly from same script correctly this minute and download a corrupt file the next minute, from the "same exact" script. Assuming the file I am downloading is named video.mp4, whenever the corrupt issue occurs, the file will downoad as video.php – Ekene Jun 21 '17 at 19:02
  • let me know if you would like me to post my solution that works for me? – Fight Fire With Fire Jun 22 '17 at 21:43
  • @FightFireWithFire Yes please. Another project or someone else might need it here too. Thanks for sharing – Ekene Jun 24 '17 at 18:49
0

I've been using PHP for most of my web apps, which usually included some form of download or another. They all worked fine until my most recent project. Videos/other files are uploaded correctly, but are corrupted on download. In order to inspect the problem, I used Burp Suite to intercept/inspect web traffic and here's what I got:

Image showing unwanted space in Video file

Notice the number of blank lines in the response at the right of the image (after "Content-Length: 11028102"). In HTTP standard, only one blank line (new line character) is to be left before the response body (which in this case is the video--what appears to be garbled text from line 21). In other words, the response body/video text should have started at line 13.

Initially, I thought it was a server problem, so I changed from Apache running on LAMPP to Nginx. Still problem persisted. I then supposed it was a PHP readfile() function problem, so I set up a Node Server on port 3000 strictly for download. So PHP /download page processes the request and fetches from Node server (which is JavaScript). I thought it'd be sufficient and faster since Node server does not create a new thread for each new connection unlike PHP and many other languages. It worked out well, the video file started from line 13 (in my case) as it ought to, and the files were no longer corrupted, plus it was very fast. However, this isn't a probable solution.

Simple Solution: So recently, I got curious again as regarding this problem, and inspected my index page using Burp Suite again and noticed similar occurrence to what was corrupting files: unnecessary space before the HTML as can be seen in the image below:

Image showing index file wrongly returned

The same space was there but the HTML comment was showing correctly starting at line 12. This was the top code portion of my index file:

<!--header-->
<?php
   require_once "core/init.php";
   require_once "header.php";
?>

This showed that the comment was output normally, starting from line 12 but there was space before the < !DOCTYPE html> contained in "header.php". Apparently, it was only "init.php" which sat between the HTML comment and the "header.php" where the HTML code starts. THIS HAD TO MEAN SOMETHING WAS WRONG WITH MY "init.php file

I then opened the file and noticed several blank lines corresponding in number to the blank lines that were output on Burp Suite, as shown in this image:

Image showing culprit blank lines in "init.php"

I simply deleted the blank lines and my files (in this case videos) were properly outputted as shown in this image:

Image showing properly outputted file from PHP

So, if you've got corrupted files on download, this is the problem. On that page, remove ANY SPACE before your PHP tags, check the files included on that page directly or indirectly using PHP, e.g., "init.php", "function files", "database connection files"; make sure there is NO SPACE before the PHP opening tag AND AFTER THE PHP closing tag. Clear all these blank lines and you're good to go! Good luck with your projects!

Holyfield
  • 31
  • 4