I write a script to force download mp3 files from a site. The code is working very fine but the problem is that it can't download large files. I tried it with a file of 9.21mb and it downloaded correctly, but whenever i try to use the code to download a file of 25mb, it simply gives me a cannot find server page or The website cannot display the page. So i now know it has problems downloading large files. Below is the code snippet that does the downloading of files.
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
header("Content-type: application/force-download");
header("Content-Disposition: attachment; filename=\"".$dname.".mp3\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($secretfile));
$downloaded=readfile($secretfile);
The displayed error is: HTTP 500 Internal Server Error
thank u very much for ur time guys.
It could be memory limits, but usually PHP will output an error saying that the memory limit has been reached.
Also, before all of that you should disable output compression if it's enabled:
if(ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', 'Off');
}
Sometimes IE can screw up if output compression is enabled.
Watch your PHP configuration for memory limits and timeouts
In php.ini :
memory_limit = 32M
max_execution_time = 300
Note that if you want to go really high in execution time you also need to change your web server timeout.
i simply gives me a cannot find server page or The website cannot display the page
Is this the error as displayed by Internet Explorer? Do you get any server-side errors? Did you check your server logs?
Try this:
// empty output buffer
while (ob_get_level()) {
ob_end_clean();
}
if (ini_get('output_buffering')) {
ini_get('output_buffering', false);
}
// function to encode quoted-string tokens
function rfc2822_quoteString($string) {
return '"'.preg_replace('/[^\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\x7F]/', '\\\$0', $string).'"';
}
// HTTP headers
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.rfc2822_quoteString($dname.'.mp3'));
header('Content-Length: '.filesize($secretfile));
// send file
readfile($secretfile);
exit;
Related
The download method I am using:
I have a "Export" button in HTML which when clicked sends some data to the server.
The server processes the data, writes the CSV to a file and whether the writing was successful or not, sends an appropriate JSON message.
On receiving a success message, I do a window.open with the proper url and attached file name to start the download.
The download happens on Firefox in Ubuntu 15.04 alright as well as IE 11 on Windows 7.
However the download fails on Google Chrome 45.0.2454.85 m on Windows 7. It just opens a new blank window and then nothing happens. I have browser cache cleared. What is the right way to start a file download for Chrome?
Here is the PHP code responsible for the download.
header("Content-Description: File Transfer");
header("Content-Type: text/csv");
header("Content-Disposition: attachment; filename=". basename($filename));
header("Expires: 0");
header("Cache-Control: must-revalidate");
header("Pragma: public");
header("Content-Length: ". filesize($filename));
ob_clean();
flush();
readFile($filename);
Try write all your headers after ob_clean().
ob_clean() clear all data with headers.
I have a CSV file on my server. If a user clicks on a link it should download, but instead it opens up in my browser window.
My code looks as follows
<a href="files/csv/example/example.csv">
Click here to download an example of the "CSV" file
</a>
It's a normal webserver where I have all of my development work on.
I tried something like:
<a href="files/csv/example/csv.php">
Click here to download an example of the "CSV" file
</a>
Now the contents of my csv.php file:
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=example.csv');
header('Pragma: no-cache');
Now my issue is it's downloading, but not my CSV file. It creates a new file.
.htaccess Solution
To brute force all CSV files on your server to download, add in your .htaccess file:
AddType application/octet-stream csv
PHP Solution
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=example.csv');
header('Pragma: no-cache');
readfile("/path/to/yourfile.csv");
Or you can do this using HTML5. Simply with
<a href="example.csv" download>download not open it</a>
This cannot be done reliably, since it's up to the browser to decide what to do with an URL it's been asked to retrieve.
You can suggest to the browser that it should offer to "save to disk" right away by sending a Content-disposition header:
header("Content-disposition: attachment");
I'm not sure how well this is supported by various browsers. The alternative is to send a Content-type of application/octet-stream, but that is a hack (you're basically telling the browser "I'm not telling you what kind of file this is" and depending on the fact that most browsers will then offer a download dialog) and allegedly causes problems with Internet Explorer.
Read more about this in the Web Authoring FAQ.
Edit You've already switched to a PHP file to deliver the data - which is necessary to set the Content-disposition header (unless there are some arcane Apache settings that can also do this). Now all that's left to do is for that PHP file to read the contents of the CSV file and print them - the filename=example.csv in the header only suggests to the client browser what name to use for the file, it does not actually fetch the data from the file on the server.
Here is a more browser-safe solution:
$fp = #fopen($yourfile, 'rb');
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
{
header('Content-Type: "application/octet-stream"');
header('Content-Disposition: attachment; filename="yourname.file"');
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($yourfile));
}
else
{
header('Content-Type: "application/octet-stream"');
header('Content-Disposition: attachment; filename="yourname.file"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
header("Content-Length: ".filesize($yourfile));
}
fpassthru($fp);
fclose($fp);
Configure your server to send the file with the media type application/octet-stream.
This means that your browser can handle this file type.
If you don't like it, the easiest method would be offering ZIP files. Everyone can handle ZIP files, and they are downloadable by default.
Nice clean solution:
<?php
header('Content-Type: application/download');
header('Content-Disposition: attachment; filename="example.csv"');
header("Content-Length: " . filesize("example.csv"));
$fp = fopen("example.csv", "r");
fpassthru($fp);
fclose($fp);
?>
A previous answer on this page describes how to use .htaccess to force all files of a certain type to download. However, the solution does not work with all file types across all browsers. This is a more reliable way:
<FilesMatch "\.(?i:csv)$">
ForceType application/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
You might need to flush your browser cache to see this working correctly.
If you are doing it with your application itself... I hope this code helps.
HTML
In href -- you have to add download_file.php along with your URL:
<a class="download" href="'/download_file.php?fileSource='+http://www.google.com/logo_small.png" target="_blank" title="YourTitle">
PHP
/* Here is the Download.php file to force download stuff */
<?php
$fullPath = $_GET['fileSource'];
if($fullPath) {
$fsize = filesize($fullPath);
$path_parts = pathinfo($fullPath);
$ext = strtolower($path_parts["extension"]);
switch ($ext) {
case "pdf":
header("Content-Disposition: attachment; filename=\"" . $path_parts["basename"]."\""); // Use 'attachment' to force a download
header("Content-type: application/pdf"); // Add here more headers for diff. extensions
break;
default;
header("Content-type: application/octet-stream");
header("Content-Disposition: filename=\"" . $path_parts["basename"]."\"");
}
if($fsize) { // Checking if file size exist
header("Content-length: $fsize");
}
readfile($fullPath);
exit;
}
?>
To force download you may use Content-Type: application/octet-stream header, which is supported by most browsers:
function downloadFile($filePath)
{
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
}
A BETTER WAY
Downloading files this way is not the best idea especially for large files. PHP will require extra CPU / Memory to read and output file contents and when dealing with large files may reach time / memory limits.
A better way would be to use PHP to authenticate and grant access to a file, and actual file serving should be delegated to a web server using X-SENDFILE method (requires some web server configuration):
X-SENDFILE is natively supported by Lighttpd: https://redmine.lighttpd.net/projects/1/wiki/X-LIGHTTPD-send-file
Apache requires mod_xsendfile module: https://tn123.org/mod_xsendfile/ On Ubuntu may be installed by: apt install libapache2-mod-xsendfile
Nginx has a similar X-Accel-Redirect header: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/
After configuring web server to handle X-SENDFILE, just replace readfile($filePath) with header('X-SENDFILE: ' . $filePath) and web server will take care of file serving, which will require less resources than using PHP readfile.
(For Nginx use X-Accel-Redirect header instead of X-SENDFILE)
Note: If you end up downloading empty files, it means you didn't configure your web server to handle X-SENDFILE header. Check the links above to see how to correctly configure your web server.
I have used following code to download approximate 920MB file,
set_time_limit(0);
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("X-Sendfile: $zipname"); // For Large Files
header("Content-type: application/zip");
header("Content-Disposition: attachment; filename=\"".$zipname."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($directory_location . '/' . $zipname));
ob_end_flush();
readfile($directory_location . '/' . $zipname);
Before this code i did some study with the following links Using X-Sendfile with Apache/PHP, Streaming a large file using PHP and Limit download speed using PHP but not much helpful to me because file download still takes more time with just (2MB) file. It's not showing and transfer rate or anything else. I want download start to serve file with around 60Kbps, with all files (Large or small)
UPDATE: One more thing i noticed its not showing any download process just executing and after sometime display the pop-up to choose the location, and after hitting save button its direct save to the computer without any downloading process window :(
Please help me to guide the right way.
Based on above comments there are two solutions:
1) Just download the file directly. You don't appear to be doing any validation, so if not, then just pass the user to the file to download and let apache handle it.
2) If you do need validation / pre-processing, then check mod_xsendfile - adding the header isn't enough, you actually need to add the mod to apache. If you're in Linux then compile from source (https://tn123.org/mod_xsendfile/). If you're not in Linux then mod_xsendfile for Win x64? has a response from the author saying he can provide binaries - but that's in 2010. There's a bit of advice around the web - although it's been a while since I looked at it so can't really help much more.
A PHP application is offering binary data as a download:
header("Content-Type: application/octet-stream");
header("Pragma: public");
header("Cache-Control: private");
header("Content-Disposition: attachment; filename=\"$filename\"");
header("expires: 0");
set_time_limit(0);
ob_clean();
flush();
#readfile($completefilename); exit;
$completefilename is a stream like "ftp://user:pwd#..."
The size of the data can be several MByte. It works fine, but sporadically I get the following error:
It's most likely that the remote stream is occasionally down, or times out.
Also as #fab says it could be that the file you are trying to load is larger than your script's memory.
You should start logging the errors readfile() returns, e.g. using the error_log php.ini directive.
If this needs to be completely foolproof, I think you'll have to use something more refined than readfile() that allows to set a timeout (like curl, or readfile with stream context options).
You could then catch any errors that occur while downloading, and serve a locally hosted fallback document instead. That document could e.g. be a text file containing the message "Resource xyz could not be loaded".
Do you have anything in your error logs?
Maybe PHP is running out of memory because readfile() needs to pull the while file into memory. Make sure memory_limit is larger than the largest file you work on with readfile(). Another option is to output the file in chunks using fread().
Many users of my site have reported problems downloading a large file (80 MB). I am using a forced download using headers. I can provide additional php settings if necessary. I am using the CakePHP framework, but this code is all regular php. I am using php 5.2 with apache on a dedicated virtual server from media temple, CentOS Linux. Do you see any problems with the following code:
set_time_limit(1500);
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"" . basename($file_path) . "\"");
header("Content-Length: ".$content_length);
header("Content-Transfer-Encoding: binary");
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private', false);
header('Pragma: public');
header('Expires: 0');
//Change this part
$handle = fopen($file_path, 'rb');
while (!feof($handle))
{
echo fread($handle, 4096);
ob_flush();
flush();
}
fclose($handle);
exit;
Basically, the problem being reported is that the download starts and then stops in the middle. I was thinking it was a problem with the time limit, so I add the set_time_limit code. I was using the php readfile function before, but that also did not work smoothly.
The problem with PHP-initiated http transfers is that they seldomly support partial requests:
GET /yourfile HTTP/1.1
Range: bytes=31489531-79837582
Whenever a browser encounters a transmission problem, it will try to resume the download. Your php script does not accomodate for that (it's not trivial, so nobody does).
So really avoid that. Redirect users to a static file and let your webserver handle it. If you need to handle authorization, use tricks like symlinks or rewriterules that check for session cookies or even a static permission file (./allowed/178.224.2.55-file-1). Any required extra HTTP headers can be injected likewise, or with a .meta file.
I don't see any trouble, but for S&G's try placing the set_time_limit inside the while loop. This ensures they don't hit a hard limit and (as long as the client's taking the information) the time-limit gets extended.