Headers & exit() causing an unresponsive form submit - php

Problem After downloading a CSV once, the form does not send a request on submission.
Purpose: To download a CSV file of information after choosing a few criteria for the download.
PHP Scripts:
export.php displays a HTML Form allowing the user to select certain criteria (which basically sets a couple of variables and then submitting those criteria in order to download the CSV file.
exportEntrants.php uses those variables set by the form to retrieve the appropriate data from the database, create the CSV data and then provide it as a download to the user.
So in export.php we have the following snippet:
require_once 'exportEntrants.php';
if(/*the form is submitted*/){
export($criteria);
}
else{
// Display the HTML Form
}
Which successfully calls the exportEntrants.php script when the form is submitted the first time.
Then in exportEntrants.php the data is created correctly and downloaded using:
function download($content,$title) {
header('Content-Description: File Transfer');
header("Content-type: application/csv");
header('Content-Disposition: attachment; filename="'.$title.'.csv";');
echo $content;
exit();
}
After which, the download works fine, and the CSV is correct. However on-screen the form is still visible to the user, so clicking the Submit button once more should trigger the if statement described above and allow me to download the file again with the same or different criteria as I see fit.
This doesn't happen though, nothing happens. The form is still displayed on screen, nothing has changed on screen at all, but submitting the form does not reload the page or cause any action.
Can anyone see what could be causing this problem? I will provide any additional information as needed.
Thank you.
EDIT in response to Comment from Hakre below.
Disabling Javascript made no difference.
This issue has been tested in Firefox 7.0.1 and Chrome 14. I have not yet tried IE or Safari.
The HTML of the form is as follows (I haven't included the criteria parts of the form as they work fine and the form is generated by a rather complicated function so it's not a small piece of HTML (lots of divs and spans):
<form action="/admin/export/export.php" method="post" name="exportForm" onsubmit="return formSubmit()"><input type="submit" name="submit_butt" value="EXPORT" class="button"></form>

The fact that the request is not being sent, when you press button repeatedly, seems to indicate an issue in client side. Check the formSubmit() function or page source of the client-side and see if maybe the attachment download somehow messed up Javascript.

#Tom Houdmont as I understand your question:
The file download scripts generally based on header function.
Using header() function has a condition that you can only send header information and can not echo or can not send any kind of text(space too) before or after it. Otherwise it will show you the warning: that header() information is already sent
Based on your code i tried one example:
In your case form action and download script, both are on same page so when your form is post check that any kind (even a space) data is printed before header() starts i.e or tag, or space. or you should cover whole ... in PHP if else statement.
I have uploaded simple image download script. here is link. You can download and run.
http://code.google.com/p/mixexperiments/downloads/detail?name=imagedownload.rar&can=2&q=
if still you dont get it, make me more clear, I will try my best

The problem you describe in your question is not related to the code you've posted.
I'm unable to reproduce your problem:
<?php
if ($_POST)
{
$title = 'test';
$content = 'Example file.';
header('Content-Description: File Transfer');
header("Content-type: plain/text");
header('Content-Disposition: attachment; filename="'.$title.'.txt";');
echo $content;
exit();
}
?>
<form method="post" action=""><input type="submit" name="submit" value="download"></form>
Test for yourself. Hope this helps to clarify where not your error is at least.

Related

Filename incorrect in save dialog - Firefox

I am redirecting to an image with a Location header from PHP, and in firefox when you view the image and right click to save it prompts to save with the name of the PHP redirect script, not the name of the image. This behaviour is not present in the other browsers.
Here is the code of the file:
<?php
header("Location: foo.jpg");
Is there anyway to get firefox to use the correct name when a user opens the save dialog?
jewlhuq's suggestion to bypass php altogether works.
<?php print("<script>window.location='image.jpg';</script>"); ?>
Using php to read the file's contents and dump those to the browser with the proper headers including the following
header('Content-Disposition: inline; filename="desired-filename.jpg"');
also works.
Which is better depends on your application. I used the first for the problem listed above, in another application I needed to serve an image with a different file name than the one it is actually saved with, for that I needed the latter.

A download script and statcounter working together without opening a new page

I have this download script running that allows people to click on a button and download a file without any new html pages opening. It all works well. Here is the script.
<?php
header('Content-disposition: attachment; filename=brochure-company-details.pdf');
header('Content-type: pdf');
readfile('brochure-company-details.pdf');
?>
However, I really want to be able to run the statcounter script at the same time, so as to document who is downloading the file. The Statcounter script works well enough when an html page opens and runs through its function. But I down't want a new page to open. I just want the download to start. The user experience will be that they only have the file download and without them knowing the statcounter will record the event.
Can you help?
What you want is not possible. (executing javascript on pdf download) Statcounter is an script and is executed by the browser by being included on the html. If you download a pdf file, you are not executing any js.
However, statcounter can see what links are pressed and therefore you can find which files were downloaded; as long your files are downloaded by using regular links on an html that has statcounter included. You don't need to do anything at all, they would be counted by default.
The idea Dachi gave, is to add an sql insertion to that php code of yours.
<?php
$dbh = new PDO("sqlite:/path/to/database.sdb");
//Put the insertion code here.
//Insert things like the IP, the login name, the time, etc.
header('Content-disposition: attachment; filename=brochure-company-details.pdf');
header('Content-type: pdf');
readfile('brochure-company-details.pdf');
That works too, and is a good choice if you are allowing downloads by not using regular links or not from an html that has statcounter.

php header Content-disposition

I have a very basic question illustrated by the code snippet below.
This is the relevant part of a much bigger program in which I wish to download a zip file. The code as shown, with the comments in place, produce the expected browser output "Download the file now."
When I un-comment the code, the zip file is correctly downloaded to my browser!
However, the browser output is not produced. How do I regain control? I would like for the user to then have other options. (By the way, un-commenting the single "Content-disposition" line is sufficient to cause the loss of control.)
I have tried including the code, putting it in a function, many possible combinations of ob_start, ob_end flush, etc., all to no avail. I am sure I am overlooking something very fundamental and would appreciate some suggestions.
Thanks.
<?php
$sZipFN = 'file.zip';
// header("Content-type: application/zip");
// header("Content-disposition: attachment; filename=$sZipFN");
// readfile($sZipFN);
$sMsg = "Download the file now.";
?>
<html>
<body>
<p> <?php echo $sMsg; ?> </p>
</body>
</html>
You can't - you have to redirect to the options page and then start the download via javascript redirect (document.location = 'http://download.url/';)
This will start the download and leave the user on the options page like you desire.
Don't forget to include a 'Click here if the download fails to start' link somewhere near the top of the page (just in case javascript is disabled)

Javascript handling of php readfile octet stream

OK, I've had a good read through the "Related Questions" section and I haven't found this answer.
I'm using an ajax request to force a php download. Things are working fine on the PHP end. Let's say I've got a stream of data called DATA. Now, I want to pop up a "Save as..." dialog.
The browser has received a string of hex values. Now, what do I do with this DATA on the client (javascript) side?
This is the PHP code that I'm using, per the link above:
header('Content-type: application/octet-stream');
header('Content-Disposition: attachment; filename=$file');
readfile($file);
I've tried
window.open(DATA) -> hex stream doesn't exist (of course)
submitting a form with the action as DATA -> same problem
BTW, If I echo the file from PHP then use window.open, it works sometimes. But not for txt files or jpgs etc.
I've seen this working on other sites - how are they doing it? Thanks in advance.
Here's the answer I was looking for:
window.open("downloadPage.php");
...which pops up a box every time. The problem with the ajax request was that the returned file stream was interpreted as XMLHttpRequestObj.reponseText.
The browser apparently interprets this differently and doesn't allow the download.

Download CSV file using "AJAX"

I'm trying to accomplish a fairly simple task for my website, but I"m not sure exactly how to go about it. I want the user to be viewing a table, then click a button, at which point the user can save the contents of that table as a csv file. This request can sometimes be quite complicated so I generate a progress page to alert the user.
I have most things figured out except actually generating the csv file. (I use jQuery and PHP)
the jQuery code run on click:
hmis_query_csv_export: function(query_name) {
$.uiLock('<p>Query Loading.</p><img src="/images/loading.gif" />')
$.get({
url: '/php_scripts/utils/csv_export.php',
data: {query_name: query_name},
success: function(data) {
$.uiUnlock();
}
});}
the relevant PHP:
header("Content-type: text/x-csv");
header("Content-Disposition: attachment; filename=search_results.csv");
//
//Generate csv
//
echo $csvOutput
exit();
What this does is sends the text as the PHP file, but it's doesn't generate a download. What am I doing wrong?
If you are forcing a download, you can redirect the current page to the download link. Since the link will generate a download dialog, the current page (and its state) will be kept in place.
Basic approach:
$('a#query_name').click(function(){
$('#wait-animation').show();
document.location.href = '/php_scripts/utils/csv_export.php?query_name='+query_name;
$('#wait-animation').hide();
});
More complicated:
$('a#query_name').click(function(){
MyTimestamp = new Date().getTime(); // Meant to be global var
$('#wait-animation').show();
$.get('/php_scripts/utils/csv_export.php','timestamp='+MyTimestamp+'&query_name='query_name,function(){
document.location.href = '/php_scripts/utils/csv_export.php?timestamp='+MyTimestamp+'&query_name='+query_name;
$('#wait-animation').hide();
});
});
At PHP script:
#header("Last-Modified: " . #gmdate("D, d M Y H:i:s",$_GET['timestamp']) . " GMT");
#header("Content-type: text/x-csv");
// If the file is NOT requested via AJAX, force-download
if(!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest') {
header("Content-Disposition: attachment; filename=search_results.csv");
}
//
//Generate csv
//
echo $csvOutput
exit();
The URL for both requests must be the same to trick the browser not to start a new download at document.location.href, but to save the copy at the cache. I'm not totally sure about it, but seems pretty promising.
EDIT I just tried this with a 10MB file and it seems that val() is too slow to insert the data. Hurrumph.
Okay, so I gave this one another go. This may or may not be completely insane! The idea is to make an AJAX request to create the data, then use the callback to insert the data into a hidden form on the current page which has an action of a third "download" page; after the insertion, the form is automatically submitted, the download page sends headers and echoes the POST, and et voila, download.
All the while, on the original page you've got an indication that the file is being prepared, and when it finishes the indicator is updated.
NOTE: this test code isn't tested extensively, and has no real security checks (or any at all) put in place. I tested it with a 1.5MB CSV file I had laying about and it was reasonably snappy.
Index.html
<a id="downloadlink" href="#">Click Me</a>
<div id="wait"></div>
<form id="hiddenform" method="POST" action="download.php">
<input type="hidden" id="filedata" name="data" value="">
</form>
test.js
$(document).ready(function(){
$("#downloadlink").click(function(){ // click the link to download
lock(); // start indicator
$.get("create.php",function(filedata){ // AJAX call returns with CSV file data
$("#filedata").val(filedata); // insert into the hidden form
unlock(); // update indicator
$("#hiddenform").submit(); // submit the form data to the download page
});
});
function lock(){
$("#wait").text("Creating File...");
}
function unlock(){
$("#wait").text("Done");
}
});
create.php
<?php
//create $data
print $data;
?>
download.php
<?php
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: text/x-csv");
header("Content-Disposition: attachment;filename=\"search_results.csv\"");
if($_POST['data']){
print $_POST['data'];
}
?>
The best way to accomplish this is to use a Data URI as follows:
Make the AJAX call to the server as per normal
Generate the CSV on the server-side
Return the data (either bare or inside a JSON structure)
Create a Data URI in Javascript using the returned data
Set window.location.href to the Data URI
See this link for instructions (paragraph #3, specifically): http://en.wikipedia.org/wiki/Data_URI_scheme
This way, you don't need to save any files on the server, and you also don't need to use iframes or hidden form elements or any such hacks.
I don't think you can make the browser download using a AJAX/JS request. Try using a hidden iframe that navigates to the page which generates the CSV
Well the point of using AJAX is to avoid a visible reload of the page. If you want a download, you want the opposite,- a brand new request from the browser. I'd say, just create a simple button pointing to your php page.
To echo and expand on what others have said, you can't really send the file using AJAX. One of the reasons for this is (and someone correct me if I'm wrong on this, please) that the page you're currently on already has sent its content headers; you can't send them again to the same window, even with an AJAX request (which is what your PHP file is attempting to do).
What I've done before in projects is to simply provide a link (with target="_blank" or javascript redirect) to a separate download PHP page. If you're using Apache, check out mod_xsendfile as well.

Categories