What code do you need to add in PHP to automatically have the browser download a file to the local machine when a link is visited?
I am specifically thinking of functionality similar to that of download sites that prompt the user to save a file to disk once you click on the name of the software?
Send the following headers before outputting the file:
header("Content-Disposition: attachment; filename=\"" . basename($File) . "\"");
header("Content-Type: application/octet-stream");
header("Content-Length: " . filesize($File));
header("Connection: close");
#grom: Interesting about the 'application/octet-stream' MIME type. I wasn't aware of that, have always just used 'application/force-download' :)
Here is an example of sending back a pdf.
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
header('Content-Transfer-Encoding: binary');
readfile($filename);
#Swish I didn't find application/force-download content type to do anything different (tested in IE and Firefox). Is there a reason for not sending back the actual MIME type?
Also in the PHP manual Hayley Watson posted:
If you wish to force a file to be downloaded and saved, instead of being rendered, remember that there is no such MIME type as "application/force-download". The correct type to use in this situation is "application/octet-stream", and using anything else is merely relying on the fact that clients are supposed to ignore unrecognised MIME types and use "application/octet-stream" instead (reference: Sections 4.1.4 and 4.5.1 of RFC 2046).
Also according IANA there is no registered application/force-download type.
A clean example.
<?php
header('Content-Type: application/download');
header('Content-Disposition: attachment; filename="example.txt"');
header("Content-Length: " . filesize("example.txt"));
$fp = fopen("example.txt", "r");
fpassthru($fp);
fclose($fp);
?>
None of above worked for me!
Working on 2021 for WordPress and PHP:
<?php
$file = ABSPATH . 'pdf.pdf'; // Where ABSPATH is the absolute server path, not url
//echo $file; //Be sure you are echoing the absolute path and file name
$filename = 'Custom file name for the.pdf'; /* Note: Always use .pdf at the end. */
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 to: https://qastack.mx/programming/4679756/show-a-pdf-files-in-users-browser-via-php-perl
my code works for txt,doc,docx,pdf,ppt,pptx,jpg,png,zip extensions and I think its better to use the actual MIME types explicitly.
$file_name = "a.txt";
// extracting the extension:
$ext = substr($file_name, strpos($file_name,'.')+1);
header('Content-disposition: attachment; filename='.$file_name);
if(strtolower($ext) == "txt")
{
header('Content-type: text/plain'); // works for txt only
}
else
{
header('Content-type: application/'.$ext); // works for all extensions except txt
}
readfile($decrypted_file_path);
Related
While writing a PHP-Script, im stuck at an issue i cant resolve.
The PHP-Script consist in letting a user download a .mp4 file. The download works without any issues but the file downloaded can not be played.
Heres the code:
<?php
$filepath = "/www/servermedia/technounion.mp4";
$filename = basename($filepath);
header("Content-type: video/mp4");
header("Content-Disposition: attachment; filename=.$filename");
readfile($filename);
exit;
?>
After the .mp4 file gets downloaded, it cannot be played.
It looks like this:
The error message means that Windows Media Player cannot play back the file because probably the player doesnt support the codec. I already tried with VLC but it does not work either.
EDIT:
Comparing both file sizes, the downloaded file is only a couple bytes large instead of the 3,73 MB of the file on the server
Your code is not well-formed, you miss to escape double-quotes by adding single-quotes as I done here, please test my answer.
<?php
$filepath = "/www/servermedia/technounion.mp4";
$filename = basename($filepath);
header('Content-Type: video/mp4');
header('Content-Disposition: attachment; filename="' . $filename . '"');
readfile($filename);
exit;
?>
But I suggest a more complex way:
<?php
$filepath = $_SERVER['DOCUMENT_ROOT'] . "/www/servermedia/technounion.mp4";
$filename = basename($filepath);
header('Content-Type: video/mp4');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . sprintf("%u", filesize($filepath)));
set_time_limit(0);
$fh = fopen($filepath, "rb");
while (!feof($fh)) {
echo fgets($fh);
ob_flush();
flush();
}
fclose($fh);
exit;
?>
$_SERVER['DOCUMENT_ROOT'] is useful to get the full path from the server
set_time_limit(0) is useful to avoid any timeout during download
fgets() is useful for reading large files
ob_flush() and flush() assure that there is not other output in the buffer
I hope this helps.
Is the downloaded file the exact same filesize?
Does the content type exist in your webserver?
header("Content-Type: video/mp4"); Note capital 'T' for type.
This maybe worth testing with to see you can serve the file content inline:
http://www.phpmind.com/blog/2016/10/how-to-use-php-to-output-an-mp4-video/
I have to trigger a download of a zip file ( The Zip file is inside my data folder).
For this i am using the code,
$file = 'D:\php7\htdocs\Project\trunk\api\data\file.zip';
header('Content-Description: File Transfer');
header('Content-type: application/zip');
header('Content-disposition: attachment; filename=' . basename($file) );
readfile($file);`
This is working in core php as i expected. But when i am using the same code in the Zend prints a content like below,
PKYsVJ)~�� study.xlsPKYsVJs�����+
tutorial-point-Export.xlsPKYsVJn��� 8��Zabc.xlsP
In between the content i can see the name of all files in the zip. But it is not getting downloaded.
After i realised that this is not working i started searching about it and Found some solution from stack over flow
Try 1: Adding different header element and ob functions in every random lines
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . $file_size);
ob_start();
ob_clean();
flush();
All these are tried from different stack overflow Question and answers and have the same result
Try 2:PHP is reading file instead of downloading . This question do not have any accepted answer (He was asking about the core php but i have the same issue with zend only) . I tried all of this but it was not working.
Try 3:Changing the .htaccess . After that i thought it was a problem with my .htaccess and found this answer for changing the .htaccess file.
<FilesMatch "\.(?i:zip)$">
ForceType application/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
This also given me the same result.
Try 4:Using download functions in Zend . I have tried the all the zend functions in the answer of this question. But given me an empty output even the file was not read.
Try 5: Remove all the unwanted spaces before and after the php tag as per the answer
Is there any other way to trigger a download in ZF2 framework?
EDIT
Below is my exact function. This is GET(API) function,
public function getList(){
try{
//here i am getting the zip file name.
$exportFile = $this->getRequest()->getQuery('exportid','');
$file = 'D:\php7\htdocs\Project\trunk\api\data\\' . $exportFile . '.zip';
header('Content-Description: File Transfer');
header('Content-type: application/zip');
header('Content-disposition: attachment; filename=' . basename($file) );
readfile($file);
return new JsonModel(["status"=>"Success"]);
} catch(\Exception $e){
return new JsonModel(["status"=>"Failed"]);
}
}
There are two problems here:
your browser trying to open the file, instead of downloading it.
also, it is not opening the file correctly.
Both point to a Content-Type error. Verify that the Content-Type being received by the browser is correct (instead of being rewritten as, say, text/html).
If it is, change it to application/x-download. This might not work in Internet Explorer, which performs some aggressive Content-Type sniffing. You might try adding a nosniff directive.
Additionally, after a readfile (and you might be forced to return the file's contents instead of readfile()'ing - i.e., return file_get_contents($filename);), you should stop all output with return null;. ZIP file directory is at the very end, so if you attach a JSON message there, you risk the browser neither downloading the file, nor displaying it correctly.
As a last resort, you can go nuclear and do everything yourself. Extremely non-elegant, and all frameworks ought to provide an alternative, but just in case...
// Stop *all* buffering
while (ob_get_level()) {
ob_end_clean();
}
// Set headers using PHP functions instead of Response
header('Content-Type: application/x-download');
header('X-Content-Type-Options: nosniff');
header('Content-Length: ' . filesize($filename));
header('Content-Disposition: attachment; filename="whatever.zip"');
die(readfile($filename));
It's possible that some creative use of atexit handlers or destructor hooks might mess up even this last option, but I feel it's unlikely.
Based on this SO answer, you can try the following modification to your function.
public function getList(){
try{
//here i am getting the zip file name.
$exportFile = $this->getRequest()->getQuery('exportid','');
$file = 'D:\php7\htdocs\Project\trunk\api\data\\' . $exportFile . '.zip';
if (file_exists($file)) {
$response = new \Zend\Http\Response\Stream();
$response->setStream(fopen($file, 'r'));
$response->setStatusCode(200);
$response->setStreamName(basename($file));
$headers = new \Zend\Http\Headers();
$headers->addHeaders(array(
'Content-Description' => 'File Transfer',
'Content-Disposition' => 'attachment; filename="' . basename($file) .'"',
'Content-Type' => 'application/zip',
'Content-Length' => filesize($file)
));
$response->setHeaders($headers);
return $response;
//return new JsonModel(["status"=>"Success"]);
} else {
return new JsonModel(["status"=>"Failed. No such file in \"".$file."\""]);
}
} catch(\Exception $e){
return new JsonModel(["status"=>"Failed"]);
}
}
This worked for me!
ob_clean(); // Clear any previously written headers in the output buffer
$filepath = "some_file.zip";
$content_type = 'application/octet_stream';
$filetype = filetype($filepath);
$filename =$filepath;
if($filetype=='application/zip')
{
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
$fp = #fopen($filepath, 'rb');
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
{
header('Content-Type: '.$content_type);
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-Transfer-Encoding: binary");
header('Pragma: public');
header("Content-Length: ".filesize(trim($filepath)));
}
else
{
header('Content-Type: '.$content_type);
header('Content-Disposition: attachment; filename="'.$filename.'"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
header("Content-Length: ".filesize(trim($filepath)));
}
fpassthru($fp);
fclose($fp);
}
If you correct the capitalisation of the headers does it work? ie use Content-Disposition and Content-Type over Content-disposition and Content-type respectively?
Regardless, as standard debugging technique I would suggest using your browser dev tools to inspect the requests that are being made (inc headers) and comparing that to what ends up in your serverside code, and what is in the server side response and what ends up in the client. I would also validate this using a private-session (Incognito mode in Chrome etc) or a fresh profile / VM install just to eliminate anything else.
Also, why not use xsendfile and delegate the responsibility of sending the file to the web server so you aren't incurring the responsibility in your PHP code? You can do this with appropriate server configuration (sometimes through .htaccess, but in this day and age surely you have complete control anyway) and then simply setting the X-Sendfile header as per the example on the above link:
header("X-Sendfile: $path_to_somefile");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$somefile\"");
Because you are return JsonModel so your output will be a json with your message instead of buffering for downloading.
Edit: I notice that you was missing Content-Transfer-Encoding: Binary, tested on my os x - php5.6 env.
You should try this
public function getList(){
try{
//here i am getting the zip file name.
$exportFile = $this->getRequest()->getQuery('exportid','');
$file = 'D:\php7\htdocs\Project\trunk\api\data\\' . $exportFile . '.zip';
header('Content-Description: File Transfer');
header('Content-type: application/zip');
header('Content-disposition: attachment; filename=' . basename($file));
header("Content-Transfer-Encoding: Binary");
header("Content-length: " . filesize($file));
header("Pragma: no-cache");
header("Expires: 0");
readfile("$file");
} catch(\Exception $e){
return new JsonModel(["status"=>"Failed"]);
}
}
Just remove your JSonModel on response.
You can try this for downloading the file instead of readfile();
Server side -
file_put_contents("file.zip", fopen("http://someurl/file.zip", 'r'));
Client side -
<button>download file</button>
download file
Here's my code:
<?php
$download = './downloads/'.$mp3;
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=".basename($download));
header("Content-Type: audio/mpeg");
readfile($download);
?>
On android, once download starts, it downloads an unreadable htm file. $mp3 is like this: NAME.mp3. What may be wrong?
Hmmm, have you looked at your PHP error logs? This sounds to me like your server / dev environment doesn't have PHP running correctly.
I found this post relating, seems they were able to get it to work with some extra headers, that is if your PHP is working properly:
https://stackoverflow.com/a/2367506/2080324
Yes, but remember headers tell devices what type of file it is. Different devices will interpret different files in different ways. In the example I linked, he sets the mime-type as well as headers:
$dir = dirname($_SERVER['DOCUMENT_ROOT'])."/protected_content";
$filename = $_GET['file'];
$file = $dir."/".$filename;
$extension = "mp3";
$mime_type = "audio/mpeg, audio/x-mpeg, audio/x-mpeg-3, audio/mpeg3";
if(file_exists($file)){
header('Content-type: {$mime_type}');
header('Content-length: ' . filesize($file));
header('Content-Disposition: filename="' . $filename);
header('X-Pad: avoid browser bug');
header('Cache-Control: no-cache');
readfile($file);
}else{
header("HTTP/1.0 404 Not Found");
}
This question already has answers here:
Forcing to download a file using PHP
(10 answers)
Closed 9 years ago.
When I am downloading an image it's displaying these characters on screen.
�M\cIDATx��\i��v�U�ݳ��x���6`8�Y�&#��#D �����HDʟ'�~�z;a���D,�c������ƌ���ٺ�*�w�=շjz^z�ـ���UwWWݺ�;�|g��&H���o�Gy���~s��K۲e�y�7�ѣG�̙3ͤI������3�O=�T���n�z�)�k�.�p�B��NJ��6m���a(�ٳgOt��i���|����hhh((�˦����{����3��sO�C�:�_�k�������s�)�|X�V��_C�6���2o~G���3����y۶m��o4�\s�) r�ܹse.|-[�,�`~/N� GQdU(h!Z� |E���J��h�&�u�sާ�ES�T���æ��ׯh�o�>���ˡ�ˠ�-��-m�{ �\p�#Mou�֙���{�qN;,�ŋ�cߏ䦦&3u�T���)��MS�yrP���B|o���Ԏ�l1�C&L8�����j�j/_4ϯ���4��o�o��w�>|8�5���M�P�;f���͆ Ҿ&N�hJM%C�,Z�� Z=�9螞�=~��4�7H�������#���-"0�jWW�1���{[[[�:;'�Θ1�0�+�^h�h�>��#�Aw�;�� ��Ba�J%�ޓ���;�����с� ���4�2�<xͩt�8��="" p�ٴi�����="" 4g�q8��:��u�9q:v�i��x(���r�b��m��n��f�ml���="" ���)��n�="" ="" -��="" ���o����+�itj�_⧟~"Ȣ="" Ԯj��`�!�x��sn��n�g��'�j��cmv��o="" !�����?������r���p��(��)�,Ԭ^�z�0k֬���ŀh�`�5�'��1�����&\��+�2�o="" �v��4�="" �ac="" ��="" 4�f+�e�="" �ӭ�w����j���q�#�)��t̟??��x4�7e���oh�6o���r&�lh�z�n��l�8��pn���kἎbhj�e�-�|!�*���ɪ��z="">�E��Ŵ��;���Ʃ�0����j\�]o^�X�A�qο�8cVh�Q�M�x��F](L3�#'f�T�*4�IxZ,K8��͜ ! ���S��MJ�h� 5��2��p�!��wۇ�n� �M�/Z�c�=&Q��_h&�8� �X8��.��鑪&uRL���b�j�~Wg���A �d#MUG(�+�B_r�$�h�w ���i 2���ʠ>�QƸ�v�e�n�~|�fm�1� D��6K�w{����z��7T0�����}���ĩ#��Q�8K�Q�"�8�^2��d�N�+l�$j3�j����h'�x�V.��qmA�����P�?[� ^bIFE�Q����#�{i���o��� �:�<&��Y���Ѳ%�L�U��܍�����ź�ZB�\���*N��X� G!*=w�J#-���k5� m��\ 7�8O~��,��=�݄}Jp�?�P�L)�P��j4F�����"Ds:��I�o���^{M����*4H#�
And I used this code for downloading
$fn = $path.'/'.$file_name;
$mm_type="application/octet-stream";
header("Cache-Control: public, must-revalidate");
header("Pragma: hack");
header("Content-Type: " . $mm_type);
header("Content-Length: " .(string)(filesize($fn)) );
header('Content-Disposition: attachment; filename="'.$file_name.'"');
header("Content-Transfer-Encoding: binary\n");
readfile($fn);
I have tried with your code and found no problem. After read your comment and try with your file thetexturemill.com/wp-content/uploads/2013/07/dell.png I have this code working:
# my demo value in my local machine
$path = dirname(__FILE__) . "/demo";
$file_name = "Capture.PNG";
#$fn = realpath($path.'/'.$file_name);
$fn = "http://thetexturemill.com/wp-content/uploads/2013/07/dell.png";
//var_dump(readfile($fn));
$mm_type="application/octet-stream";
#$mm_type=mime_content_type($fn);
#echo $mm_type; die();
ob_get_flush();
header("Cache-Control: public, must-revalidate");
header("Pragma: hack");
header("Content-Type: " . $mm_type);
#header("Content-Length: " .(string)(filesize($fn)) );
header('Content-Disposition: attachment; filename="'.$file_name.'"');
header("Content-Transfer-Encoding: binary\n");
readfile($fn);
flush();
die();
What problems I found are:
If you use an image from remote host, make sure you can get it (the allow_url_fopen INI option is ON and the returned value from readfile is greater than zero) and do not use filesize as well as mime_content_type functions.
I don't know whether thetexturemill.com is your domain name or folder name. Supposed that it is a domain name, remember to add the protocal prefix (http:// as in example)
Do not output anything before the header function calls or your downloaded file will not be open properly.
Ah, for local file, your original code work without errors on my machine.
The content type is wrong
application/octet-stream
Ocet-stream is used for executable files which images are not for sure.
A proper type for a image for jpg image is for example:
image/jpeg
You can use mime_content_type() to get proper content type of file
Returns the MIME content type for a file as determined by using information from the magic.mime file.
Try this code
$fn = $path.'/'.$file_name;
$mime = mime_content_type($fn);
header('Content-Type:'.$mime);
header('Content-Length: ' . filesize($fn));
readfile($fn);
Try this,
<?php
$fn = $path.'/'.$file_name;
$mm_type="application/octet-stream";
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Content-Length: " .(string)(filesize($fn)) );
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Disposition: attachment; filename="'.$fn.'"');
header("Content-Transfer-Encoding: binary\n");
ob_clean();
flush();
readfile($fn);
exit;
?>
Read this http://php.net/manual/en/function.readfile.php
I have a folder with many files of different types. I need to display any requested file, so the header has to be adapted accordingly. What I tried is:
header( 'Content-Type: ' . filetype( 'files/' . $file ) );
readfile( 'files/' . $file );
It works for JPG, PNG and GIF files, but for some reason MP3 files already crash the browser, so I suppose it doesn't work for every file type. Is there something wrong with the approach, or the code? How would you do it? Thanks!
By the PHP Documentation, filetype() Returns the type of the file. Possible values are fifo, char, dir, block, link, file, socket and unknown.
What you want is the MIME type of the file. What you want is Fileinfo. For example:
$finfo = finfo_open(FILEINFO_MIME_TYPE);
header("Content-Type: ".finfo_file($finfo, 'files/'.$file));
finfo_close($finfo);
readfile('files/'.$file);
You can also quickly try mime_content_type, if available, just to make sure there is nothing else wrong with the system. But this is not recommended because it is depricated.
You have to send the correct mime type and that can be tricky.
You can use the mime-content-type function, but that is a deprecated function.
$mimetype = mime_content_type ( $file );
header( 'Content-type: '. $mimetype );
use this for mp3 files
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);