I'm running the following code to download a file in the backround instead of just redirecting to the file in the browser, but I can't click on anything else on the page until the whole file is downloaded. Anything I can change in the code to fix that?
When clicking on a link this is loaded in a separate php file, so the user doesn't see where the file is fetched from ($filepath) and it's also protected by a session:
header("Cache-control: private");
header("Content-Type: application/octet-stream");
if ($download) {
header("Content-Disposition: attachment; filename=\"" . $title . "\"");
header('Content-Transfer-Encoding: binary');
} else {
header("Content-Disposition: inline; filename=\"" . $title . "\"");
}
// Disable caching
header('Cache-Control: no-cache, no-store, must-revalidate'); // HTTP 1.1.
header('Pragma: no-cache'); // HTTP 1.0.
header('Expires: 0'); // Proxies.
readfile($filepath);
Edit // I still haven't been able to figure this out. Now I'm trying with this AJAX code but it still locks the whole page until the file is fully downloaded and it only loads in the background, not to the harddrive.
$('.download').on('click', function(e) {
e.preventDefault();
var self = $(this);
$.ajax({ type: 'POST',
url: self.attr('href'),
async: true,
success : function(response)
{
}
})
});
Disclaimer: all below is valid for the default file-based sessions. For other handlers it may not stay true.
It's sessions that block: as soon as one page has the session open no other page for the same session id would be served.
The solution would be to close the session as soon as you're done with it.
So put
session_write_close();
right before you started serving your file.
It would close the session for the page that serves that file and other pages can be served after that, even though the file is not downloaded fully yet.
References:
http://php.net/manual/en/function.session-write-close.php
Related
simple question I guess, but can't figure it out... Here's the problem:
In the front end, I export some user-selected data to the server to dynamically create a file:
$("#export-button").click(function() {
$.post("'.$postUrl.'",{variable:exportSelection},
function(data) {
console.log(data);
}
);});
Then, after I receive the data and create/save the file on the server, I am doing the following in php:
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;}
What am I supposed to do in the post callback function instead of printing on the console in order to get a download prompt?
UPDATE: OK - I just realised that my #export-button was an anchor tag with no href... Now when I point to the file on the server, the problem is that when I click, it follows the link prompts to save file etc., but it gets to the file BEFORE the "new" version is generated (so using the previous selection at each click)
OK - I figured it out. I just needed to disable the link from pointing anywhere and then do:
window.location = 'my_file_location.doc';
... inside the callback function
Okay so this is how I rewrote it. Any changes? Will this work? I've added the Javascript at the end with the timeout.
if (file_exists($file)) {
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');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
}
?>
<script type="text/Javascript">
window.setInterval(function()
{
window.location = "www.moneymovers.com";
}, 5000);
</script>:
You can't download and then do a header redirect. The client is going to redirect as soon as it sees the header, which means the download won't work. But you also can't output the header after the download, because then the redirect won't happen. Either way, it's not gonna work.
I would recommend doing this at a different level. For example, when the user clicks the download link, have some JavaScript start the download in a new tab/window and then redirect to the desired location in the current tab.
You can put the location header wherever you want, as long as nothing has been outputted (echo).
If you have outputted something, you can output something like this to redirect :
<script type="text/javascript">
window.location = "www.example.com";
</script>
EDIT :
In your case, it is not possible to do what you're looking for, as far as I know. You will have to manage this case in the caller page. The Javascript will not be called because you've modified the Content-Disposition.
I guess I have to break down and ask for help. (Should have done it 3 days ago!)
Here's what happens...
PHP reads session & post variables, builds a .csv file from a mysql query.
it attempts to open a 'Save As' dialog box and when that's done, jump to another page.
I'm using nested functions but when run, the dialog box seems to get run over and never appears.
separately the functions work fine.
when run, the 'save as' dialog box doesn't wait for user input
Can anyone see what I've done wrong or can you redirect my thinking?
$filename points to the created CSV file on the server
$suggname is a default filename users should see in the dialog box.
The code:
holdit($filename,$suggname);
function holdit($filename,$suggname) {
$fp=#fopen($filename, 'rb');
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")) {
header('Content-Type: "application/octet-stream"');
header('Content-Disposition: attachment; filename="'.$suggname.'"' );
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($filename));
} else {
header('Content-Type: "application/octet-stream"');
header('Content-Disposition: attachment; filename="'.$suggname.'"' );
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
header("Content-Length: ".filesize($filename));
}
fpassthru($fp);
fclose($fp);
jump();
}
function jump() {
header('Location: return_from_csv.php');
}
You are adding lots of headers to your HTTP response. One of those is Location which instructs the browser to redirect. Obviously it is interpreting that as a higher priority than your other headers.
Decide if you want to redirect or serve a file in your response and do one or the other.
I suspect you have misunderstood the Location header. Read this: http://en.wikipedia.org/wiki/HTTP_location
By the looks of things you are trying to serve the CSV file and then redirect to another page. Sorry, you cannot do this. An HTTP response does one thing and one thing only. You might consider opening your link to the CSV file in another window using the target attribute of <a>.
I have a grid whith the data that come from a database. Each row of the grid containt these fields : document name, owner, version etc. The content of the document is in a blob.
I want, when the user double-click on a row, to download the content of the document. The best way will be a pop-up that ask a confirmation. The grid must stay on the screen.
I'm able to read the content of the document with an ajax request and put the result in a variable.
Ext.Ajax.request({
url: '../controleurs/c_get_doc_content.php',
method: 'POST',
params: { dep: r.data.nom_document },
success: function(response, opts) {console.log(response);console.log(opts)},
requestcomplete: function (conn, response, options) {},
failure: function() {console.log('failure');},
headers: { 'my-header': 'foo' },
How can I save the content of the document to a file on the user PC ?
The best solution for this is server side. Instead of passing the blob to the JS, you just pass an ID. Then when the user chooses to download the file, you call the server and it returns a document that can be downloaded.
You would just stream that content as an attachment instead of sending it with the AJAX request
in PHP you use the following code to make sure a file is downloaded and the save as dialog is displayed.
if (file_exists($file)) {
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);
exit;
}
Set the content type of your PHP response to something the browser would want to save....
I'm at a loss with an annoying issue to do with exporting a report. Basically, a button is pressed and a report is generated server side using the following javascript:-
__callExportController(true, { op: 'build', type: exportType }, function(data) {
var outputURL = './reportinc/export_controller.php?op=output&filename=';
var reportFilename = data['filename'];
var reportTitle = data['title'];
if (reportFilename && reportTitle) {
var resultURL = outputURL + reportFilename + '&title=' + reportTitle;
/* Initiate the download dialog */
if (!$('#exportFrame').length) {
var hiddenIFrame = document.createElement('iframe');
hiddenIFrame.setAttribute('id','exportFrame');
document.body.appendChild(hiddenIFrame);
}
$('#exportFrame').attr('src', resultURL);
} else {
error('No filename or report title specified!');
}
});
The 'build' operation of the export controller builds the report to a temporary file on the server. If that succeeds, the 'output' operation is called to output that file to a hidden iframe in order to get the download prompt to the user. Internet Explorer 6/7 are the only browsers in use here.
This is the output handler on the server which the iframe will be requesting with the successfully built filename:-
/* Output handler */
case 'output':{
$filename = $_GET['filename'];
header('Content-Description: File Transfer');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Type: application/pdf");
/**
* NOTE: It appears this is required for some versions of adobe!
* http://www.acrobatusers.com/forums/aucbb/viewtopic.php?id=15400
*/
header("Cache-Control: private");
header("Pragma: cache");
header("Content-Disposition: attachment; filename=\"file.pdf\"");
header('Content-Length: ' . filesize($filename));
/* Flush the headers immediately for larger files */
ob_clean();
flush();
readfile($filename);
#unlink($filename);
}
The issue I'm having is: whilst this works fine once, the session appears to be destroyed after the first successful file download. That is, when the user navigates away to another page they appear to be generated a new session id. This also requires the user to have to 're-login' if basic authentication is in use with the next action they take.
The issue seems very intermittent and it seems to happen at times and not at other times.
Has anyone any ideas? Should I be adding more headers or something to prevent the users session from being destroyed?
if the top level domain of the iframe is not the equal the Ie will revert to the p3p protocol and deletes the session. adding a header will fix this issue.
http://weblogs.asp.net/coltk/archive/2010/10/13/session-lost-in-iframe-p3p-issue.aspx
could be your missing a session_name or session_start somewhere.
or, more likely, you’re users have cookies disabled. that way sessions are only valid for one site request