I’ve got some PHP code set up to send various mp3 files under certain conditions. While it works to send the audio data, the mp3 file’s duration isn’t reported (so, for example, it’s reported as a ‘live broadcast’ when viewed in Safari). Linking directly to the file works properly, so I presume the issue is to do with my PHP code and HTTP headers.
Currently, to send the file, I’m doing this:
header("Content-Transfer-Encoding: binary");
header("Content-Type: audio/mpeg");
header(“Content-length: “ . filesize($filePath));
header(“Content-Disposition: attachment; filename=$filename”);
header(“Cache-Control: no-cache”);
readfile($filePath);
die();
Essentially I just want the PHP to deliver the file in exactly the same way as if I linked directly to the mp3 file itself. Any ideas on what I’m doing wrong would be appreciated!
Edit: Solved! Figured out the issue and described in the comment below.
Related
I have a script that merges 2 documents and then it shows a final document in a browser.
It works in Firefox, Opera and IE. But it does not work in Chrome.
Chrome only shows loading and it stops in 1/4 of loading.
The code:
exec("pdftk A=$pdfin B=$tmpfname cat B1 A output $tmpfoutput");
$data = file_get_contents($tmpfoutput);
header("Content-type: application/pdf");
header("Content-disposition: inline;filename=GeneratedPdf.pdf");
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($tmpfoutput));
header('Accept-Ranges: bytes');
echo $data;
I have been told that it also worked in Chrome before so I think there will be only a problem with the last version.
Thanks for any help.
By appending Accept-Ranges: bytes, your script tells the browser that it will accept range requests, i.e. multiple requests that request a part of the response. Your script obviously does not support range requests because it generates and provides the data at once.
To fix the error, remove header('Accept-Ranges: bytes');
If your PDF files are usually large, then a more user-friendly solution is to actually implement range requests in your script. Odds are that your server does already have an efficient routine that handles range requests, so a smart choice is to save PDF file to a publicly accessible directory, then 302-redirect the request to this URL after the PDF has been generated. Make sure that the URLs are unguessable, e.g. by using UUIDs. And remove the PDF files at some point, e.g. using a cronjob.
I'm serving an .mp3 file (as a download dialog box, where the end-user can rename and save the file). There are a lot of conflicting reccomendations for this on the forums and even within the comments on the php manual.
I hope to solve my issue and create here a reference on how to best execute this goal: i.e. a clear "best practices summary" usable by begining php coders on the most efficient way to get started delivering downloadable files.
I would really appreciate it if you'd check over the code here and suggest your corrections--this is part of a site that helps artists self-publish music, books, etc.
To test, I am uploading this to my server as index.php; perhaps that is also part of the problem.
The current status of this script is that the browser hangs a bit and then loads the binary as text into the browser display window.
I've thought at many points that my problem was syntax in the important "Content-Length" header, or the order of my headers, so I've tried several versions of all that, but none cut off the download.
Here is the exact code that I am now trying on my own,
where ####### means 7 numbers (the actual file size in bytes),
and everything else should be clear:
<?php
header('Server: ');
header('X-Powered-By: ');
header('Cache-Control: no-cache');
header('Expires: -1');
header('Content-Description: File Transfer');
header('Content-Transfer-Encoding: binary');
header('Content-Type: audio/mpeg');
header('Content-Disposition: attachment;filename="suggested-save-name.mp3"');
header('Content-Length: #######["exact-file-name.mp3"]');
#readfile("http://full-public-url.com/exact-file-name.mp3");
ob_clean();
flush();
exit;
?>
Returns these response headers:
>X-Powered-By:PHP/5.4.xy
>Vary:negotiate
>Transfer-Encoding:chunked
>TCN:choice
>Server:Apache
>Keep-Alive:timeout=2, max=200
>Date:Fri, 10 May 2013 16:mm:ss GMT
>Content-Type:text/html
>Content-Location:filename.php
>Connection:Keep-Alive
I hope it is a simple error (or more than one) in the syntax of my script, or the way I created and saved the .php file. I have verified the settings are at default, php is up to date, and there are no .htaccess issues. I have carefully made sure there are no extra spaces at the end of this file, as well as all other files in the web directory, and as well I've tried the script with and without the closing ?>.
Thank you in advance
...
Best script after reading Answers, below:
<?php
$file-variable=('./exact-file-name.mp3')
$size=filesize($file-variable);
header('Server: ');
header('X-Powered-By: ');
header('Cache-Control: no-cache');
header('Expires: -1');
header('Content-Type: application/octet-stream'); //will need to redirect for older IE
header('Content-Disposition: attachment; filename="suggested-save-name.mp3"');
header('Content-Length: "$size"');
#readfile("$file-variable");
ob_clean();
flush();
exit;
Successful response headers, from the better script (& after removing the UTF-8 BOM):
>Vary:negotiate
>TCN:choice
>Server:Apache //How can I hide this?
>Keep-Alive:timeout=2, max=200
>Expires:-1
>Date:Sat, 11 May 2013 12:mm:ss GMT
>Content-Type:audio/mpeg
>content-transfer-encoding:binary
>Content-Location:filename.php //I would also like to obfuscate this
>Content-Length:#######
>Content-Disposition:attachment; filename="suggested-save-name.mp3"
>Content-Description:File Transfer
>Connection:Keep-Alive
>Cache-Control:no-cache
Step 1 would be to remove all nonsense headers that are not defined in the HTTP standard.
This will eliminate Content-Description and Content-Transfer-Encoding. Both are useless at best, and might interfere with normal browser operations in the worst case.
Step 2 is to optimize the file delivery. Do not download the MP3 with a HTTP request, access the FILE on the server. Do not use a URL, do use a file path. If the MP3 is right next to your script, this will work:
readfile('./exact-file-name.mp3');
At this point you should usually end up with a working download. If not, try changing the Content-Type to something more generic. audio/mpeg might trigger the audio player in some browser, application/octet-stream should work in most browsers but older Internet Explorer, which do inappropriate content sniffing on certain mime types including this one. application/x-ms-download is supposed to work then.
Make sure the header is sent. PHP does not send HTTP headers if the HTTP body was already startet, and any whitespace including the UTF-8 BOM will trigger body output.
Some comments on your "final" headers in general:
Content-Length: should only have an integer stating the length in bytes, nothing more. Especially no mention of any filename in square brackets.
Content-Transfer-Encoding and Content-Description are still useless.
Content-Location is not needed. If you don't know what it does, omit it. Obfuscation will not work here, the browser needs to know the URL he is accessing. Duplicating this URL in this header does not change anything, obfuscating it will likely break things somewhere.
The two headers you really only need for a download are: Content-Type and (if you want to pre-define a filename for the user) Content-Disposition.
I use POST requests to transfer data that is needed to create the downloadable document on the fly.
When the form is submitted, the $_POST array is passed to a function that returns the path of a created FDF file.
The code:
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="'.$file.'"');
passthru("pdftk file.pdf fill_form ".$fdf_file." output - ");
exit;
Work on all browser (Safari, Firefox, Opera, Chrome, Internet Explorer) but not in Android stock browser.
In Android browser, the download taking place but the content of the file being empty or some HTML garbage, or the browser downloading the file but ignoring my file name and trying to save the file under the name of the script that generated it.
Any help is much appreciated.
Thanks!
UPDATE!!!
the solution: http://digiblog.de/2011/04/19/android-and-the-download-file-headers/
Try sending the binary encoding header:
header("Content-Transfer-Encoding: binary");
If still not, you may have to send the file under a different file type.
the solution:
Android and the HTTP download file headers
http://digiblog.de/2011/04/19/android-and-the-download-file-headers/
I have an XML feed generated by a PHP script with mod_rewrite running. IE opens the feed OK but I cannot get it to save the file to disk or import to Excel.
The file I am trying to access in the url is similar to:
http://domain.com/download/export.xml
This gets written to a download PHP script and is not actually an XML file.
After setting the headers as below, it is still not possible to get IE to save the file. Also, as it is not capable of displaying the plain source, copy & paste into notepad will not work because of various styling changes IE makes to the XML.
<?php
header("Content-type: text/xml");
header('Content-Disposition: attachment; filename="QuoteExport_'.$quoteDate.'_'.$quoteSlot.'.xml"');
Does anyone know the solution? Thanks.
Edit:
Thanks for the help so far. I have tried a combination of these headers and still not getting the results I want. No matter what I set, IE always displays it in-line in the browser with no option to download. The save functionality also still does not work either. Any other ideas how to force IE to save the XML as a file by using headers?
Edit2: The state now is that IE gives the option to open/save but whilst trying to save, there is a popup saying that it is "Unable to open this internet site.". Yet at the same time, the open option displays the content with no issues.
To force ie and other browsers to download you have to specify specific headers like in this example :
header("Content-disposition: attachment; filename=\"".$fileName . ".csv\"");
header("Content-Type: application/force-download");
header("Content-Transfer-Encoding: binary");
header("Pragma: no-cache");
header("Expires: 0");
Other wise if you use text/xml, ie thinks he knows how to display it and does so.
You need to set a session parameter for some versions of IE. Assuming you have $my_file_name and $my_file_contents set, here's how it would look:
if(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
session_cache_limiter("public");
}
session_start();
header('Content-type: application/octet-stream');
header('Content-Disposition: attachment; filename=".' $my_file_name '. "');
print($my_file_contents);
(Adapted from this article)
Try setting the content-type to application/xml or application/force-download
After spending much time changing the headers, I went for a slightly different approach. No matter what I was setting them to, it just would not work for me.
Anyway the solution implemented was:
When a client lands on the URL (http~://my-domain.com/downloads/export.xml), the PHP script generates a real XML file in the same location as the request-URI and saves it on the server, schedules it for a delete job in the future and then effectively refreshes the browser. Then, the mod-rewrite rules on the second request serve the actual file and then IE can use the file correctly. A future request then causes the cycle to run again.
Slightly round about way of doing things, but it was the fastest method. Also, I had the ability to schedule file operations from another part of the application making life much easier for clean-up!
Anyway thanks for the help and I did learn a thing-or-two about header options.
I have a script that generates data in csv format which is sent to the user along with a set of headers that tell the browser it is a .csv file. Everything works great when users (left)click on the link to the script, they are presented with a download dialog with the filename ending in .csv and it suggests using excel, or calc, to open it. However, when users right-click and choose Save As it is being saved with the php script name.
Here is the header code:
header("Pragma: public");
header("Expires: 0"); // set expiration time
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
$val = date("m_d_Y_g_i");
Header('Content-Disposition: attachment; filename="personal_information_'.$val.'.csv"');
So again, when users left-click it saves the file as personal_information_date.csv; when they right click it saves as download.php. I'm using FF3. Oddly enough, IE7 does not have this problem.
Any ideas?
Use mod_rewrite to alias the file from file.csv to file.php, this is a browser issue rather than PHP because by saving the file it isn't running it before it is saving it.
So to summarise:
Link to personal_information_date.csv
Create a mod_rewrite rule that forwards personal_information_date.csv to download.php (e.g.: RewriteRule ^personal_information_date.csv$ download.php).
The HTTP client may ignore more than one content type header, the two other will be ignored - which of them? Depends on the browser implementation, therefor the different behaviour. The correct mime type is text/csv, not application/octet-stream! The content-disposition header is correct for the download.
I believe that setting three different mimetypes doesn't help
what's $val ? Is this known content or user provided - e.g. could it contain nasty characters (like ") or even linebreaks, e.g. introduce new HTTP header lines?
have a look at the HTTP-Headers that arrive at the client. Either the Firefox built-in information or use LiveHttpHeaders (plugin to be found at the Mozilla site - logs all HTTP-Headers) - I'm sure there are more/other plugins for FF available.
Hope this helps.