I'm trying to stream an mp3 file with PHP and play it on the browser.
I'm using Ubuntu for both the server ( apache ) and client for testing. My code works on Chrome, but not on FireFox.
When I access the mp3 directly ( so it's served by the web server ) it works on FireFox as well, but comparing the headers that the web server generates with the headers I send in PHP I couldn't find how to fix the problem. ( I'm spying the headers using FireBug )
Here are the webserver generated headers ( That does work ):
Accept-Ranges bytes
Connection Keep-Alive
Content-Length 490265
Content-Type audio/mpeg
Date Sun, 11 Mar 2012 04:01:45 GMT
Etag "22064e-77b19-4badff4a88200"
Keep-Alive timeout=5, max=100
Last-Modified Sat, 10 Mar 2012 09:15:52 GMT
Server Apache/2.2.20 (Ubuntu)
Here are the headers that are sent to the browser from my PHP script:
Accept-Ranges bytes
Cache-Control no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection Keep-Alive
Content-Length 490265
Content-Type audio/mpeg
Date Sun, 11 Mar 2012 04:16:00 GMT
Expires Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive timeout=5, max=100
Pragma no-cache
Server Apache/2.2.20 (Ubuntu)
X-Powered-By PHP/5.3.6-13ubuntu3.6
This is the code I use to stream the mp3:
header('Content-length: ' . filesize($path));
header('Content-Type: audio/mpeg');
header('Accept-Ranges: bytes');
readfile($path);
exit;
I did also tried other headers which didn't help, such as:
header('Content-Disposition: inline; filename="name.mp3"');
header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
header('Pragma: no-cache');
header('Cache-Control: no-cache');
But like I said, none of these fixed the problem.
Many thanks for any help,
Oded.
EDIT:
OK this appears to be extremely strange. After much debugging, I made sure that the headers and content of the PHP version and the webserver versions are the same, and then I found out what breaks it, but I have no idea why. Here is the scenario that breaks it:
1) Store a string of a path in $_SESSION in a previous script.
2) Read this string in the script that streams the mp3.
3) Use this string as the path to load the mp3 file.
If I do that, FireFox cannot play the file, when I press on the mp3 player, it prints a "GstDecodeBin2: This appears to be a text file" message.
If I hard code the path instead of using the $_SESSION, it works. The crazy thing is that I made absolutely sure that the path in the $_SESSION is correct! Remember that the headers and content of the PHP and webserver versions are identical!
The HTTP Accept-Ranges header allows the browser to send a starting and ending point of the file to download, this allows for multi-part downloading of the same file. There are plenty of PHP implementations of this, here is one found on the PHP.net documentation page for fread().
http://www.php.net/manual/en/function.fread.php#106999
I found what the problem is using WireShark to monitor the requests. Earlier I used FireBug and HTTPFox, and they don't show all the requests!
WireShark showed me that after the initial successful request there is another request for the same URI. This second request was not caught by xdebug, and was missed by FireBug, and HTTPFox. The problem is that this request does not include the PHPSESSID! Obviously as a result the session did not work, and because it did work on the first request I was confused.
This seems to me like a bug in FireFox with its media player module.
I can work around this by manually adding the PHPSESSID to the URL as query string.
Related
I have a few projects where file downloads don't works anymore. It seems due to a recent Chrome policy change.
Downloads tries fails with this error in the console :
Mixed Content: The site at 'https://example.com/' was loaded over a secure connection, but the file at 'https://example.com/logs_view.php?export=true' was redirected through an insecure connection. This file should be served over HTTPS. This download has been blocked. See https://blog.chromium.org/2020/02/protecting-users-from-insecure.html for more details.
My files are served thru PHP :
header("Content-Type: application/xls");
header("Content-Disposition: attachment; filename=".$filename.".xls");
header("Pragma: no-cache");
header("Expires: 0");
echo utf8_decode($export);
It seems this method don't send the file with HTTPS headers... Any idea on how to fix this ?
You can find below the headers of the server response header :
HTTP/1.1 200 OK
Date: Tue, 19 Jan 2021 11:33:14 GMT
Server: Apache
Expires: 0
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Disposition: attachment; filename=base_changelog_2021-01-19_12-33-14.xls
Content-Length: 899
Keep-Alive: timeout=5, max=92
Connection: Keep-Alive
Content-Type: application/xls
Thanks a lot for your support.
Ben
There is nothing wrong in Your code.
When You make redirect to the url which returns file You are trying to do it from http while you are on https secure connection. Probably You have link with wrong protocol.
I find a way to force chrome to "upgrade" the download.
I added to my .htaccess this tweak, and it did the job!!!
<ifModule mod_headers.c>
Header always set Content-Security-Policy "upgrade-insecure-requests;"
</IfModule>
i have this php page that looks like this:
/get_data.php?forid=123&pdid=456&exclude_ids=
it's accessed publicly and return the same content when provided with the same variables -"GET" variables that are included in the link- ,and i can't seem to have the browser cache the page so it won't bother the server every time the page is needed!
I've tried and added this line to the head of get_data.php but no luck
header('Cache-Control: public, max-age=7200');
this is the php file code:
<?php
header('Content-type: application/json');
header('Cache-Control: public, max-age=7200');
require('config.php');
$link_database = new mysqli($host,$dbusername,$dbpassword,$database);
...
?>
accessed like this in javascript:
xmlhttp.open("GET", "/get_data.php?forid="+forid+"&pdid="+pdid+"&exclude_ids="+existing_ids.join(","), true);
tested with firefox i get this raw response headers:
HTTP/1.1 200 OK
Date: Sat, 15 Jun 2019 16:04:54 GMT
Server: Apache/2.4.25 (Debian)
Cache-Control: public, max-age=7200
Keep-Alive: timeout=5, max=91
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/json
is what i'm trying to do even a possibility?
if it is, how can i?
ps: the php code execute a simple sql, and a pure php cache system will not give any advantages(i think!).
I'm having a PHP file that is imitating a PNG file by setting the mime-type to image/png.
Thanks to a .htaccess-file that I have, I can access my image in these ways:
/img/img
/img/img.png
/img/img.php
and all of them are working well in my browser, but they don't load on my Teamspeak server.
I'm not sure what you expect to happen, these are the headers being returned:
CF-Cache-Status
MISS
CF-RAY
21cddbcc942220ae-LAX
Cache-Control
no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection
keep-alive
Content-Encoding
gzip
Content-Type
text/html; charset=UTF-8
Date
Fri, 28 Aug 2015 06:11:24 GMT
Expires
Thu, 19 Nov 1981 08:52:00 GMT
Pragma
no-cache
Server
cloudflare-nginx
Transfer-Encoding
chunked
Vary
Accept-Encoding
X-Powered-By
PHP/5.4.41-0+deb7u1
So first off, you are returning text/html. That is not the proper mime type for a .png image.
Secondarily, your script needs to actually return .png data.
I'm assuming here you plan to create the .png in your script using GD or ImageMagick, but whatever you plan to do, nothing will appear when an image is expected, when you don't actually provide the proper mime type and the proper image data that is expected to follow that mime type.
Setting the mime type at the top of your script is as easy as:
header('Content-type: image/png');
You are also going through Cloudflare. You should have Cloudflare turned off until you've debugged things and are sure that the script is operating as expected.
Thanks to #gview's answer I found out that Cloudflare was protecting the /img/ folder, so I made a page rule to stop this. Just in case you need it, this now is my script and it sets the correct headers:
# Generate $im and Stuff
# ...
# Now Output it
http_response_code(200);
$file = "./temp.png";
imagepng($im, $file);
header("Content-Type: image/png");
header("Content-Length: ".filesize($file));
readfile($file);
exit;
Does anyone know how to get PDF.js load and display pages progressively when PDF file is output by PHP ? When called directly, the PDF file loads fine and is displayed progressively but when PHP is taking care of the output of the same PDF file, PDF.js waits for the entire file to be loaded before displaying the first page. I tried different headers such as these without success :
$file = 'big.pdf';
$filename = 'fakefilename.pdf';
header('Content-type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($file));
header('Accept-Ranges: bytes');
#readfile($file);
Thanks in advance for any help.
EDIT after #Rob's comment :
here are the logs from the web browser :
Accept-Ranges 0-23822222
Cache-Control no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection Keep-Alive
Content-Length 23822222
Content-Range bytes 0-23822221/23822222
Content-Type application/pdf
Date Mon, 23 Jun 2014 13:06:33 GMT
Expires Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive timeout=15, max=99
Pragma no-cache
Server Apache/2.2.16 (Debian)
X-Powered-By PHP/5.3.3-7+squeeze19
Just adding the "Accept-Ranges" headers will not magically activate chunked responses. You have to recognize the HTTP Range header, seek in the file, then send this data together with a "206 Partial Content" status line and "Content-Range" header.
Here is sample code for streaming a MP4 file, it can almost directly be copy-pasted for PDFs. To have optimal display times, make sure that your PDF is linearized (also known as "Web optimized"). This is a feature of PDF document generators, which outputs the PDF stream in order so that all data required to render the first page(s) is available when the first chunks of the PDF file have been loaded.
I am delivering a ZIP file in 64k chunks using a loop in PHP (but the problem would arise with any server side language).
When fetching the file with FF, everything goes just fine.
When fetching the file with IE7, some bits get corrupted. This leads to an error message regarding wrong CRC (a hash) and some of the unzipped files end up being corrupted.
The headers being sent are the following:
Expires: 0
Cache-Control: must-revalidate, post-check=0, pre-check=0
Pragma: public
Content-Description: File Transfer
Content-Disposition: attachment; filename="671fb8f80f5e94984c59e61c3c91bb70.zip";
Content-Transfer-Encoding: binary
Vary: Accept-Encoding
Content-Encoding: gzip
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/octet-stream
Does anyone have a clue where this corruption comes from?
Thanks to the previous answers, i managed to solve the problem:
Apache's mod_deflate encoded the responses in gzip. This had two effects when sending the file in chunks:
The Content-Length header was not sent out
The delivered files were corrupted when using IE7
The solution, in php, is to disable the encoding of the response using the following command:
apache_setenv('no-gzip', '1');
Content-Encoding: gzip
Did you intend to gzip your (already compressed) zip? I assume your web server adds this header, but if you added it yourself with PHP then maybe this could be the problem?
This MSDN article explains that IIS encodes ZIP files using gzip, but without the proper headers, it won't decode it before sending it to the unzipping program. Firefox is probably smart enough to automatically decode it. A fix is mentioned in the article, thought the article title doesn't exactly mention your issue.
I would double-check your IIS settings just in case.