I am loading an mp3 into an html <audio> tag. The source of that tag is really a php script which returns a song that is not hosted in the public directory.
Omitting most of validation and other code, the headers used to output the mp3 are:
header( 'Content-type: {$mime_type}' );
header( 'Content-length: ' . filesize( $file ));
header( 'Content-Disposition: inline;filename="'.$filename.'"' );
header( 'Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0' );
header( 'Pragma: no-cache' );
header( 'Content-Transfer-Encoding: binary');
header( 'Expires: 0');
readfile( $file );
This works on Firefox, Chrome (and mobile), Edge (and mobile) and Opera. However I can't seem to get Safari to use the generated url as an audio source. In fact, no audio source appears in the DOM after the page has rendered:
The url might look like, www.website.com/song.php?sid=234234234234
I have tried tweaking this file plenty of times but can't see to figure out why this is happening on Safari. My intuition is the way the browser deals with mp3s and the like, which should be dealt with in the header.
Any guidance, hints or help would be appreciated.
Thanks.
update:
Added the network, shows that it loads the mp3 but then the second request doesn't?
response headers:
Name Value
Server Apache
Content-Type audio/mpeg, audio/x-mpeg, audio/x-mpeg-3, audio/mpeg3
Date Wed, 28 Dec 2016 17:24:48 GMT
Cache-Control no-cache
X-Powered-By PHP/5.3.29
Content-Disposition inline;filename="148_2793d1c49976a3689147634359577ec1aa5619f1.mp3"
Content-Length 834312
Expires 0
Connection Keep-Alive
Content-Transfer-Encoding binary
Accept-Ranges bytes
Keep-Alive timeout=5, max=100
Pragma no-cache
You need to pass the Multiple ranges header, because it's a partial content: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
header("Accept-Ranges: 0-filesize( $file )");
EDIT
Take a look here:
http://www.techstruggles.com/mp3-streaming-for-apple-iphone-with-php-readfile-file_get_contents-fail/
You need to define some headers for safari, even for the mobile version.
I had this issue sometime ago, sorry if I don't remember correctly how I fixed that, but is a header issue (its the main part I can remember, and I don't use comments for adding the answer because its too long)
Try with this..
header( 'Accept-Ranges: bytes');
header("Pragma: public");
header("Expires: 0");
header('Cache-Control: no-cache, no-store');
header('Content-Transfer-Encoding: binary');
header('Content-Disposition: inline; filename="'.$filename.'"');
header('Content-Length: '.$fsize);
header('Content-Type: audio/'.$t);
header('Accept-Ranges: bytes');
header('Connection: Keep-Alive');
header('Content-Range: bytes 0-'.$shortlen.'/'.$fsize);
header('X-Pad: avoid browser bug');
header('Etag: '.$etag);
Where:
$filename = "myaudio.mp3";
$path = 'music/'.$filename;
$fsize = filesize($path);
$shortlen = $fsize - 1;
$fp = fopen($path, 'r');
$etag = md5(serialize(fstat($fp)));
fclose($fp);
$t = "mpeg";
Hope this helps, but the 2 main headers you need is "Etag" and "Accept-Ranges" if I'm not wrong.
Related
I'm trying to display a PDF in the browser if possible--and I know I can do this in Chrome, which is what I'm testing in. The trouble is, every time I try, it prompts a download instead.
I'm using PHP sessions, so I know there are some extraneous headers being sent, so I called header_remove() to reset everything.
I call this function to show the PDF:
<?php
// For demonstrative purposes
session_start();
if (!isset($_SESSION['auth'])) {
header('Location: login.php');
die;
}
/*
* void viewPDF (Report $report)
* Outputs the PDF of the report
*/
function viewPDF ($report) {
// Tell the browser we are going to serve a PDF file.
$file = dirname(__FILE__).'/../reports/'.$report->id.'.pdf';
// The location of the PDF
if (!file_exists($file)) {
die ('The PDF does not exist.');
// Somehow the file does not exist.
}
header_remove();
// I'm using PHP sessions, so remove the headers
// automatically set that might break something.
header('Content-Disposition: inline;filename='.$report->id.'.pdf');
header('Content-Transfer-Encoding: binary');
header('Content-Type: application/pdf');
header('Content-Length: '.filesize($file));
readfile($file);
// Serve the report PDF file from the reports
// repository.
die;
// Any whitespace could corrupt the PDF, so be extra
// sure nothing else gets printed.
}
// For demonstrative purposes:
$report = new StdClass;
$report->id = 1;
viewPDF($report);
?>
These are the headers being sent:
Date: Tue, 08 Oct 2013 18:41:32 GMT
Server: Apache/2.2.22 (Win32) PHP/5.4.15
Content-Type: application/pdf
Content-Transfer-Encoding: binary
Content-Disposition: inline;filename=1.pdf
Connection: Keep-Alive
Keep-Alive: timeout=5, max=100
Content-Length: 73464
It's still prompting a download though. Once it downloads, I can open it in Adobe Reader just fine.
Am I missing something?
Thanks.
This code worked for me :
header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . basename($file).'"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
So i am generating mp3 file and it's working fine because when i download it i can play it just fine, but now what i want to do is output that file to my browser and play it in my browser, so what i have tried is:
header("Pragma: no-cache");
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Content-Type: audio/mepeg');
header("Content-Disposition: attachment; filename=\"validate.mp3\"");
header("Content-Transfer-Encoding: binary");
header("Content-length: $size");
AND
<embed autoplay="true" height="0" width="0" src="actions/play_file" />
Well ofcourse id doesn't work, it just forces to download that file because i have used
"Content-Disposition: attachment; filename=\"validate.mp3\"")
And im so sure if im using right html tag for this?
But if i am, all i need is just right headers to make this work.
Here's a way to feed up the file:
header("Content-type: audio/mpeg");
header("Content-length: " . filesize($file));
header("Cache-Control: no-cache");
header("Content-Transfer-Encoding: binary");
readfile($file);
Or in chunks
$total = filesize($filepath);
$blocksize = (2 << 20); //2M chunks
$sent = 0;
$handle = fopen($filepath, "r");
// Push headers that tell what kind of file is coming down the pike
header('Content-type: '.$content_type);
header('Content-Disposition: attachment; filename='.$filename);
header('Content-length: '.$filesize * 1024);
// Now we need to loop through the file and echo out chunks of file data
// Dumping the whole file fails at > 30M!
while($sent < $total){
echo fread($handle, $blocksize);
$sent += $blocksize;
}
exit(0);
The important thing for you to do is specify the Content-Type header. What the browser (or other user-agent) does with it is up to them, not to you. The content type you are using now is incorrect. Use audio/mpeg.
The only way to get it to always play is to include a player on a web page. For that, you can use HTML5 audio tags, Flash, embed, etc.
... and yet... Another more simpler approach ... I have tested this approach since the moment a Polish company by the name of Ivona, was purchased by Amazon on Jan. 24, 2013, so it is proven to work out of the box, after setting your AWS Polly credentials.
header('Accept-Ranges: none');
header('Content-Type: audio/mpeg');
echo $audio;
I'm using WP Audio player to steam mp3 files.
This is how I embed the player:
AudioPlayer.embed("player_new_7b4c6a94c8f3cc3f79ce7b8cc4946103", {
soundFile: "file/read/Mdjk7EGOzSzNUmNa8NYhgn6oCQwxoKhzw27EL27cgwCUUd9BLYpRIkt",
noinfo: "yes",
autostart: "no" ,
animation: "no",
buffer: 1,
remaining: "yes"
});
PHP gets the file and then outputs the contents:
$file = new File();
if($data = $file->readFromInbox($request->get('file'))
{
header("Content-Type: audio/mpeg");
header("Content-Transfer-Encoding: binary");
header('Content-Length: ' . $data->getSize());
header('X-Pad: avoid browser bug');
header("Cache-Control: no-store");
header("Cache-Control: no-store, must-revalidate");
header("Cache-Control: no-store,max-age=0,must-revalidate");
header("Cache-Control: max-age=0,must-revalidate");
header("Cache-Control: must-revalidate");
echo $data->getData();
exit();
}
else
{
$this->redirect404();
}
The whole application is using SSL. There is no standard HTTP port 80 access.
This works fine on Chrome, Firefox, Opera etc. but not for IE.
I read this post: http://faindu.wordpress.com/2008/04/18/ie7-ssl-xml-flex-error-2032-stream-error/
Apparently flash has problems opening files over SSL when using IE.
Any help would be greatly appreciated. thanks.
EDIT: This works in IE9. Just not IE7 or IE8
I found a solution.
These headers is what solved the problem:
header ("Cache-Control: no-store");
header ("Expires: -1");
header ("Pragma: public");
Plus, of course, with the standard headers:
header("Content-Type: audio/mpeg");
header("Content-Transfer-Encoding: binary");
header('Content-Length: ' . $data->getSize());
header('X-Pad: avoid browser bug');
I have a function for outputting documents, images etc:
public function direct($theMimeType, $thePath)
{
header('Content-type: '.$theMimeType);
ob_clean(); // clean output buffer
flush(); // flush output buffer
readfile($thePath);
exit;
}
It works great in Firefox. The file opens whether it is PDF, DOCX or any other file. However, in IE it freezes and nothing shows up.
What could cause this?
EDIT:
I have added few other headers:
public function direct($theMimeType, $thePath)
{
$aSize = filesize($thePath);
$aBegin = 0;
$aEnd = $aSize;
$aFilename = end(explode('/', $thePath));
$aTime = date('r', filemtime($thePath));
$aContentDisposition = ('application/pdf' === $theMimeType) ? 'inline' : 'atachment';
header('HTTP/1.0 200 OK');
header("Content-Type: $theMimeType");
header('Cache-Control: public, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Accept-Ranges: bytes');
header('Content-Length:'.($aEnd-$aBegin));
header("Content-Range: bytes $aBegin-$aEnd/$aSize");
header("Content-Disposition: $aContentDisposition; filename=$aFilename");
header("Content-Transfer-Encoding: binary\n");
header("Last-Modified: $aTime");
header('Connection: close');
ob_clean(); // clean output buffer
flush(); // flush output buffer
readfile($thePath);
exit;
}
Well, it works in IE now but still it opens the file much slower than Firefox. There seems to be few seconds freeze up before the IE browser opens the file.
Make this file download directly, not using any scripts
make sure it works in IE
in firefox, use LiveHTTPHeaders to watch what headers being sent by web-server
in firefox, use LiveHTTPHeaders to watch what headers being sent by your script
make your script's headers the same as web-server's
Most of those headers aren't really necessary. I prefer to keep things simple:
header ('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
header ('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); // always modified
header ('Cache-Control: cache, must-revalidate'); // HTTP/1.1
header ('Pragma: public');
header ('Content-Type: '.$theMimeType);
header ('Content-Disposition: '.$aContentDisposition.'; filename="'.$aFilename.'"');
header ('Content-Transfer-Encoding: binary');
header ('Content-Length: '.$aSize);
Watch out for the \n at the end of your Content-Transfer-Encoding header.
The 'Pragma: public' is a workround specifically to handle a problem with IE and https connections. The other key difference is $aFilename in quotes.
Here is my issue. I am trying to call a page: foo.php?docID=bar and return a PDF to the screen which is stored as a BLOB in the DB.
Here is the portion of my code which actually returns the PDF:
$docID = isset($_REQUEST['docID']) ? $_REQUEST['docID'] : null;
if ($docID == null){
die("Document ID was not given.");
}
$results = getDocumentResults($docID);
if (verifyUser($user, $results['ProductId'])){
header('Content-type: application/pdf');
// this is the BLOB data from the results.
print $results[1];
}
else{
die('You are not allowed to view this document.');
}
This works perfectly fine in Firefox.
However, in IE, it doesn't show anything at all. If i'm on another page (i.e. google.com), and I type in the URL to go to this page, it will say it's done, but I will still have google.com on my screen.
I checked the headers for the responses from both firefox and IE. They are identical.
Does anyone have any suggestions? Need more information?
EDIT: If it helps at all, here's the response header and the first line of the content:
HTTP/1.1 200 OK
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 349930
Content-Type: application/pdf
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: PHP/5.1.2
Set-Cookie: PHPSESSID=cql3n3oc13crv3r46h2q04dvq4; path=/; domain=.example.com
Content-Disposition: inline; filename='downloadedFile.pdf'
X-Powered-By: ASP.NET
Date: Tue, 21 Apr 2009 16:35:59 GMT
%PDF-1.4
EDIT: Also, the page which pulls out the pdf file actually uses HTTPS instead of HTTP.
Thanks in advance,
~Zack
I figured out what the issue was. It's an IE bug dealing with IE, HTTPS and addons. (See here)
It was a caching issue. When I set:
header("Cache-Control: max-age=1");
header("Pragma: public");
(see here), the PDF was in cache long enough for the adobe reader add-on to grab it.
I had this issue too, i used the following which seems to work fine
header("Content-type: application/pdf");
header("Content-Length: $length");
header("Content-Disposition: inline; filename='$filename'");
Try this:
header("Content-Type: application/pdf");
header("Content-Disposition: inline; filename=foo.pdf");
header("Accept-Ranges: bytes");
header("Content-Length: $len");
header("Expires: 0");
header("Cache-Control: private");
Also, if you are using sessions, you can try setting
session_cache_limiter("none");
or
session_cache_limiter("private");
if ( USR_BROWSER_AGENT == 'IE' ) {
header( 'Content-Disposition: inline; filename="' . $name . '"');
header( 'Expires: 0' );
header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
header( 'Pragma: public' );
} else {
header( 'Content-Disposition: attachment; filename="' . $name . '"' );
header( 'Expires: 0' );
header( 'Pragma: no-cache' );
}
This was the only header I needed to change:
header("Pragma: public");
I think you need to add more headers.
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Disposition: attachment; filename=THEFILENAME.pdf;");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . strlen($results[1]));