I'm on a shared hosting site, using Apache 2.2.22 and PHP 7.0.27, gzipped output.
Reading a file with PHP and setting the headers with
header("Content-Length: ".filesize($filename));
gives ERR_SPDY_PROTOCOL_ERROR when viewed in Chrome, with the SSL on, works without SSL.
I know that this means that the given length in the header doesn't match with the real length, but why? How can I get the correct length for the header?
The following script is trivial and works ok apache without issue
header('Content-Type: image/jpeg');
echo file_get_contents('./photo.jpg');
On NGINX/PHP-FPM I get a blank page. I have tried two different virtual servers. One I created, and the homestead improved box ( https://github.com/Swader/homestead_improved ) which is based on Laravel Homestead.
Error reporting is on, there are no errors. If I remove the header and just use:
echo file_get_contents('./photo.jpg');
I get the binary converted to ASCII and see the strange characters; the file is being loaded correctly.
I thought the issue might be a missing header, so I tried content length:
header('Content-Type: image/jpeg');
$contents = file_get_contents('./photo.jpg');
header('Content-length: ' . strlen($contents));
echo $contents;
This gives a different result: The page never loads, as if the browser never receives all the bytes it's expecting.
If I print strlen($contents) it displays the file size in bytes. PHP is loading the image correctly, but it's never reaching the browser.
The script works on an Apache server so the issue seems to be NGINX or PHP-FPM.
I have tried different images (one 80kb, one 2.2mb), the result is the same. I've also tried readfile instead of file_get_contents.
Update
In Chrome developer tools, the full image is downloaded and shown in the Network tab. The browser is getting the data but it's not displayed.
Your problem lies in process memory. PHP uses a different configuration file when running under PHP-FPM then when running under Apache for instance.
The problem with file_get_contents is that it reads the entire file into memory. In the case of an image file, memory reaches its limit and the http response never completes.
To fix the problem, you can either stream the image using fopen or increase PHP-FPM php's memory limit.
Like Scriptonomy said the memory can be an issue. You can also use readfile
https://secure.php.net/manual/en/function.readfile.php
This is driving me crazy. I can download a file from internet through browser and its working fine. However if I write a script that does this for me using PHP, the file ends up being completely different (as per online diff tools).
$link = "https://torcache.net/torrent/9AE1726935FF9C08DF422CCE3C4445FC9484478B.torrent?title=[kat.cr]the.big.bang.theory.s08e24.720p.hdtv.x264.dimension.rartv";
file_put_contents($file_name, fopen( $link, 'r'));
First I tried to play around with encoding, but that should not matter as the file is binary, right? Also tried file_get_contents instead of fopen first, the same problem.
My PHP app is running on UTF-8 w/o BOM files. Can someone help? What am I doing wrong?
I think I got what you mean. The responses from torcache.net are all compressed with gzip. If you download the file from your browser, the file is automatically decoded, but if you do the same with php you get the same file but still encoded. You can use gzdecode to decodes it.
file_put_contents($file_name, gzdecode(file_get_contents($link)));
You have some errors in your code.
First you try to add a string to a variable without parentheses.
$link = "https://torcache.net/torrent/9AE1726935FF9C08DF422CCE3C4445FC9484478B.torrent?title=[kat.cr]the.big.bang.theory.s08e24.720p.hdtv.x264.dimension.rartv"
The next one you try to download that file over https. Check if your openssl is enabled and if allow_url_fopen is set correctly in your php.ini
https://secure.php.net/manual/en/features.remote-files.php
There are 3 ways to solve your problem:
enable openssl for https calls
Use curl and set the ssl verifier to 0
Replace the https with http to make a normal call.
Option 3 is the easiest one.
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.
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
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
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.
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
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!
I'm working on a PHP script which generates large (multi-MB) output on the fly without knowing the length in advance. I am writing directly to php://output via fwrite() and have tried both standard output and using Transfer-Encoding: chunked (encoding the chunks as required) but no matter what I try the browser waits until all the data is written before displaying a download dialog. I have tried flush()ing too after the headers and after each chunk but this also makes no difference.
I'm guessing that Apache is caching the output as the browser would normally display after receiving a few kB from the server.
Does anyone have any ideas on how to stop this caching and flush the data to the browser as it is generated?
Thanks,
J
First of all, like BlaM mentioned in his comment, if in the PHP configuration OutputBuffering is enabled, it wont work, so it would be useful to know your phpinfo().
Next thing, try if it works with a big file that is stored on yor webserver, output it usinf readfile. And, together with this, check if you send the correct headers. Hints on how to readfile() and send the correct headers a provided here: StackOverflow: How to force a file download in PHP
And while you are at it, call ob_end_flush() or ob_end_clean() at the top of your script.