I'm using this bit of code to download a file (path_facture_name) from the server to the client browser :
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($path_facture_name) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Content-Length: ' . filesize($path_facture_name));
ob_clean();
flush();
readfile($path_facture_name);
ob_end_flush();
# ----
# Some other PHP code
# ----
This works just fine, but when the file is downloaded, the script is ended, and the part Some other PHP code will never be executed.
So, my question is, is there a better way to download a file from the server that don't abort the execution of the next part of the code ?
I've tried to use <iframe> or JavaScript code to redirect the window to a sipparate .php file that will handle the download. But that didn't work because this feature that I wanna add is a part of an 18 years old complex php CRM that I can't easily/freely edit.
I'm looking for a PHP solution or guidelines.
Before submitting the form (or whatever takes you to the download page) use javascript to display sg. like "File downloading, press ok" and a button. It will stay because there's no HTML content to be loaded.
Set this button to load your download page again but skipping the file download headers this time (with a get switch).
The answer for this question is #ADyson's comment above :
« "and reload the current php page"...you can't do that, only the browser can do that in this situation. You've already given your response to the request in the form of a file download. The only way to "reload the current page" from the server would be to send some new HTML for the browser to display. But you can't respond to a single HTTP request with both a file download and a HTML document. It's physically impossible. You've already set headers indicating to the browser that the response is a file for download. »
So this is a HTTP limitation. We can't do a multi-part response.
Related
I would like to trigger an action when Apache detects that a certain file URL has been started for download (or: successfully downloaded).
Example: when https://example.com/download/token_A6FZ523/myfile.zip is downloaded by a client, execute the following query to a SQLite database:
INSERT INTO downloads(date, tokenID) VALUES(CURRENT_TIMESTAMP, "A6FZ523");
Usage: then, in a PHP Dashboard, I can check who has downloaded the delivered files.
I could do this by:
running a script every minute on the server,
parsing the Apache logs /var/log/apache2/other_vhosts_access.log in search for a pattern download/token_.*/myfile.zip
execute the INSERT INTO query in this case
This seems rather complex and the fact of having to run the script every minute is not a nice solution.
What is a good solution to ask Apache to save to a SQLite database the information "The file associated to download token A6FZ523 has been downloaded by the client."?
Or maybe should PHP be used instead?
I think your problem lies in that you are directly fetching a file that is stored on the server, as opposed to using PHP to "serve" this file programatically. This isn't the first problem you will encounter with this method, you also can't check for security or get the file from external file storage (generally speaking, you don't store files directly on the web server these days!).
But, simple to do once you know how :)
Firstly, lets change the URL you download your file from to something like https://example.com/download.php?token=A6FZ523
So, we are sending a GET variable to a php script named "download.php". In that script you will have something like the following:
<?php
$token = $_GET['token'];
// Get the information about the file from the DB, something like:
// SELECT filename, size, path FROM files WHERE token = $token;
// Giving you $filename, $size and $path
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
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: ' . $size);
echo file_get_contents($path);
// This will be on a completed download
// INSERT INTO downloads(date, tokenID) VALUES(CURRENT_TIMESTAMP, $token);
?>
When the download.php file is called, the token is taken and matched to a file's info in the DB. You then set headers which basically tells the browser "this is a file", your browser responds accordingly by implementing a file download as normal. You then read the contents of the file to the user. Once this has been completed, you can log the download via another DB call.
A big thing to say is that this script (obviously with the DB calls written in) should do the very basics for you, but there is a lot more to add depending on your usage scenario. Think security, input validation, where you store your files and sending a MIME type header.
Hopefully that should point you in the right direction though :)
If you have access to server and authority to install thins you could add mod_log_sql and have the apache save the log directly into a database table (it even parse the info for you) them in your dashboard you can just do simple queries. The "thing" here it seams that you are in need to get the name of the downloader, therefore you should add that "tokenID" to your URL and set the Apache to deny the url if tokenID is not present. You would need to parse the tokenID from url in the log thought.
I was unable to find an answer regarding headers. I have a .MP4 file hosted on website A and would like to force download using a button on website B. Is it possible without downloading the file to website B and then serving it to the user?
I have already pulled all the necessary data to website B, but in order to download a file, the user needs to right click to save on the button.
Here's what I use (PHP) where $cf contains the path to the file being requested. You may want a download script stored on server B that makes this happen and just link to the script to do the download .. http://link.to.script/script.php?file=thefilename.mp4'>Download I wouldn't normally put the filename in the URL but this is just an example.
header('Content-Disposition: attachment; filename="' . basename($cf) . '"');
header("Content-Length: " . filesize($cf));
header("Content-Type: application/octet-stream");
readfile(realpath($cf));
I am using php to show the pdf
#readfile($actualfilename);
header('Content-type:pdf');
header('Content-Disposition: inline');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($actualfilename));
header('Accept-Ranges: bytes');
the problem is on the tittle bar it shows
and when I use
header('Content-type:application/pdf');
it prompts me to download the file and the same problem appears when I use
header('Content-Disposition: inline; filename='.$fakefilename.'');
This code shows pdf file in only firefox. I.E,chrome prompts me to download file?
Are you trying to change the browser's title bar text (or wherever it shows the file name)?
If so, you're out of luck here, because that won't work as it's up to the browser to decide how/where (if at all) show the file name.
However, there's some possible workaround: You could use server side tools, such as mod_rewrite in an Apache environment to redirect a request like download/readme.pdf behind the scenes to readfile.php?file=readme.pdf. In this case the browser won't know about the hidden rewrite and it will in fact display readme.pdf as the file name (even if the real file name or the script's name on the server side are different).
Hi I want to have the option on my site for the user to download a CSV file. I have used the code below
<input type="button" value="Download as CSV file" window.location.href='call_log.csv' " />
This does work but when the button is clicked the file is opened in another tab on my browser, What I want to happen is a download straight to the users default download folder
I posted this question before and the response was to include headers ie
Content-Disposition: attachment; filename="call_log.csv"
The page is a php file and if I include headers the page does not load but trys to download the whole page .
Surely I cant put headers in the CSV file , can anyone help me please ?
Thanks
Your server needs to bet set to execute your php script - you're right; there's no need to change that.
What you need to do is send the correct header to the server from your php script. Here's an example from php.net:
<?php
// We'll be outputting a PDF
header('Content-type: application/pdf');
// It will be called downloaded.pdf
header('Content-Disposition: attachment; filename="downloaded.pdf"');
// The PDF source is in original.pdf
readfile('original.pdf');
?>
For your csv file, the correct content type is text/csv
I've got a php page which handles requets for file downloads. I need to be able to detect when a file has been downloaded successfully. How can this be done? Perhaps there's some means of detecting this client-side then sending a confirmation down to the server.
Thanks.
Edit:
By handle, I mean that the page is doing something like this:
$file = '/var/www/html/file-to-download.xyz';
header('Content-Type: application/octet-stream');
header('Content-Length: ' . filesize($file));
header('Content-Disposition: attachment; filename=' . basename($file));
readfile($file);
Handle the download in a seperate php script (better do a little more than just readfile($file);, you can also provide the ability to resume downloads like in this question).
Then in this script, when you read the last block and send it, you know that all the file was sent. This is not the same as knowing that all was received, but it should be enough for most scenarios.
What Treb said, but I should add that you can check if the client is still listening during download with connection_status().
Don't forget to flush() after you've written data to the client, it helps in detecting if the connection is still up.