I wrote a download script in PHP as specified below, my script is downloading the files correctly, but I am feeling that the browser(chrome) progress bar is not getting updated properly in regular intervals.
My file is of size 320MB, while downloading that file the progress is getting updated randomly as "11MB, 76MB, 200Mb & 320MB" or "70MB & 320MB" etc.
In most of the sites download progress update is happening in constant chunks like after every MB, so I want to know how we can control the progress update intervals, may be by sending some extra headers or something else.
I want to improve the user experience by updating the progress in constant intervals, so anybody please help me to handle this situation in a proper way.
// HTTP Headers for ZIP File Downloads
// http://perishablepress.com/press/2010/11/17/http-headers-file-downloads/
// file variables
$filename = "Movie Tunes.zip";
$filepath = "files/";
// http headers for zip downloads
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: public,must-revalidate, post-check=0, pre-check=0");
header("Content-Description: File Transfer"); // MIME
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary"); // MIME
header("Content-Length: ".filesize($filepath.$filename));
ob_end_flush();
#readfile($filepath.$filename);
Thanks,
Siva
No, you cannot influence when/how the browser updates its download progress bar.
Related
I'm doing a script that increases the counter for an APK file's download then sends the file to the browser for download.
Here's what I have:
<?php
$file = "android.apk";
function force_download($file){
header("Pragma: public", true);
header("Expires: 0"); // set expiration time
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Disposition: attachment; filename=".basename($file));
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($file));
die(file_get_contents($file));
}
force_download($file
The problem is that with a browser like firefox, it downloads but it is like 'android.apk - 0 bytes'. So it essentially, it does not download the file's contents.
What might I be doing wrong? A solution for this?
IMPORTANT: It has to work on mobile.
);
I've never accessed a .apk link that didn't force a download, so I'm not sure what the need for a force download is on that. As far as incrementing the counter, I would probably just link to a page that forwards to the apk file after the counter has been done.
For instance link someone to: getapk.php?apkid=1
Then on getapk.php do something like this:
$update = mysql_query("UPDATE apps SET downloads...");
if ( $update ) { header("Location: appname.apk"); }
Of course that leaves out a lot of details, but if you need help with anything else I'd be happy to provide more details.
I've realized that I don't need to use complex header info expecially if the script will be moved from server to server where the .apk mime type is not native and may therefore be hard for a novice to set up.
A simple redirect will do:
$file_name = $_GET['f']; //$_GET['f'] has the link to the file like http://mydomain.com/file/android.apk
//Do database query or increase download counter
header('location: '.$file_name);
Voila! I have increased the counter and the download will be pushed to the browser.
I'm using the following the PHP script to download a 20mb file:
(filepath & filename are set earlier in the script)
$fullPath = $filepath.$filename;
if ($fd = fopen($fullPath, "r")) {
// http headers for zip downloads
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("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($fullPath));
while(!feof($fd)) {
echo(fread($fd, 1024*8));
flush();
if (connection_status()!=0) {
fclose($fd);
die();
}
}
}
fclose($fd);
exit();
It works fine if the download finishes, but if the download is canceled by the user, and they click on the link to re-download, the server is completely unresponsive for several minutes, and then the download will begin. It seems like it is waiting for the script to time out...but isn't the "if (connection_status()!=0)..." part supposed to kil the script?
Any help is much appreciated! Thank you in advance!
I think you're over-engineering your solution somewhat. Try using readfile() instead of your fopen/fread loop.
Better yet, unless there's a compelling reason why you need PHP to mediate the file transfer, don't use a PHP script at all and simply provide a direct link to the file in question.
The best code is always the code you don't have to write.
I am coding a file sharing application for my office. One strange problem I am going through is the Illustrator files being opened in PDF when you hit the download button.
This problem is triggered because the mime type of illustrator files is application/pdf. So the browser when it reads the file, triggers Acrobat to open the file. Is there any way I could instruct the browser to open the file in Illustrator?
Or is there any way to modify the mime type after uploading the file? The backend code is PHP.
Thank you for any help.
One way to do this is to force the browser to display the "download file"-dialog. So the user can decide what to do with the file.
This can be done via PHP-Headers. (http://www.php.net/manual/en/function.header.php#83384)
There is also an example on how to this (Post 83384):
<?php
// downloading a file
$filename = $_GET['path'];
// fix for IE catching or PHP bug issue
header("Pragma: public");
header("Expires: 0"); // set expiration time
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
// browser must download file from server instead of cache
// force download dialog
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
// use the Content-Disposition header to supply a recommended filename and
// force the browser to display the save dialog.
header("Content-Disposition: attachment; filename=".basename($filename).";");
/*
The Content-transfer-encoding header should be binary, since the file will be read
directly from the disk and the raw bytes passed to the downloading computer.
The Content-length header is useful to set for downloads. The browser will be able to
show a progress meter as a file downloads. The content-lenght can be determines by
filesize function returns the size of a file.
*/
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
#readfile($filename);
exit(0);
?>
When using this example please consider that using
$filename = $_GET['path'];
is a big security problem. You should work with something like ID's instead or validate the input.
For example:
if($_GET['file'] == 1) {
$filename = foobar.pdf;
} elseif($_GET['file'] == 2) {
$filename = foo.pdf;
} else {
die();
}
Normally, when I want to allow a user to download a file without revealing the exact location, I just use something like this to let them download the file:
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"" . $filename) . "\";");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($filename));
readfile("$filename");
But if they are using a modern browser or other download client, and they pause the download and try to resume it, the script (assuming they are still authenticated or whatever) will resend the headers and the file contents from the beginning, thus breaking the download, and basically requiring the file to be redownloaded from the beginning.
How can I enable my script to compensate for paused (and consequentially, resumed) downloads?
Use php's built-in fopen to open the file and then fseek to the right place (based on the range in the request header) and then return the partial file using fpassthru instead of using readfile.
You can find some example code in php under the comments for fread
You need to read the request headers like Range, If-Range, etc then seek to the correct location in the file. Normally a web-server would do this for you on an ordinary file. It's a bit complex but here's something that might get you started:
http://forums.asp.net/t/1218116.aspx
http://www.notes411.com/dominosource/tips.nsf/0/480C4E3BE825F69D802571BC007D5AC9!opendocument
For the second link the code is in part 12
I am downloading a file from another server. I wish to push this file to my users rather than saving it to my server.
In other words, pass them the file handle so it just passes through my server and saves to their machine. How can I do this? I have this so far:
$handle = fopen($_GET['fileURL'], 'r');
$filename = stream_get_contents($handle);
How do I push this to the user, maybe using headers?
Thank you for any help and direction.
EDIT
I have the headers:
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
header("Content-Type: $ctype");
header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile("$filename");
Its just that it doesn't push the headers. I just get a blank page after about 15 seconds which looks like it downloading the file but not giving it to me.
I wish for the script to immediately send the headers to the user as a stream.
exit();
You can try this
$filetype = mime_content_type($filename);
header('Content-type: '.$filetype);
header('Content-Disposition: attachment; filename="'.$filename.'"');
UPDATE for your EDIT:
Do you have errors disabled, since this sounds like the headers already sent error?
error_reporting(E_ALL | E_STRICT);
You don't have to use fopen() when using readfile();
Just include the filename inside readfile() like this:
readfile($_GET['fileUrl']);
Although this is very dangerous security-wise as the user could specify any file on your file server. If you only have a few files you want someone to be able to download perhaps you should store them in an array (or database, preferebly)
Here's an array example:
$files = array('file1.jpg', 'file2.png', 'file3.pdf');
//assume $_GET['file_id'] == 0, 1 or 2
if (file_exists($files[$_GET['file_id']]))
readfile($files[$_GET['file_id']]);