I am displaying a report to the client. I have made an ajax call that passes in a "delivery" variable, which is either "display" or "download".
Here is the ajax call:
$.ajax({
type: 'POST',
url: 'ajaxController.php',
dataType: 'json',
data: {
e: "getReport",
reportName: reportName,
delivery: delivery
},
success: function (data) {
if (delivery === 'display') {
$("#reportDisplayTableHeader").html('');
$("#reportDisplayTableBody").html('');
Lifestyle.selectedReportRows = data;
$.each(Lifestyle.selectedReportRows, function(key, row) {
rowHTML = '<tr>';
$.each(row, function(parameter, value) {
if (isHeader) {
rowHTML += '<td>' + parameter + '</td>';
} else {
rowHTML += '<td>' + value + '</td>';
}
});
rowHTML += '</tr>';
if (isHeader) {
$reportHead.append(rowHTML);
isHeader = false;
} else {
$reportTableBody.append(rowHTML);
}
});
$("#reportCaption").show();
}
}
});
And here is the server side PHP:
if ($delivery == 'display') {
echo json_encode($return);
} else if ($delivery == 'download') {
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=file.csv");
header('Content-Description: File Transfer');
header("Pragma: no-cache");
header("Expires: 0");
echo "record1,record2,record3\n";
}
In the case of "display" it returns the json just fine and the client side displays a table.
In the case of "download", I want it to pop up a download dialog where it can save off the CSV that I echo'd to them.
But what is happening is that the call is completing and the headers / csv is crossing the wire (thanks Fiddler), but no download dialog is appearing and the client does not know that I pushed csv to them.
What do I need to do in order to get the download dialog to pop up?
Thanks.
An Ajax call can not download something, or at least it is really hard.
Better is to open a new window to the location of the php file (Then you should be using GET though) and then the user will be promted to download it.
Related
using a function to close the connection with the browser while another process continues, is it possible to have the errors of the process in execution in a modal window opened by AJAX?
Now in the modal window I get are the value of $outputText even if the process stops.
Here are the details:
function to close browser connection
function closeOutput($outputText)
{
set_time_limit(0);
ignore_user_abort(true);
ob_start();
echo $outputText;
$size = ob_get_length();
header("Connection: close\r\n");
header("Content-Encoding: none\r\n");
header("Content-Length: $size",TRUE);
header("HTTP/1.1 200 OK");;
ob_end_flush();
ob_flush();
flush();
}
and this is the function of the process
function processmail($con,$id)
{
for($i=1; $i <= 20; $i++)
{
mysqli_query($con, "SELECT * FROM nlarea WHERE id='$id'");
sleep(2);
}
}
these 2 functions are called from this send.php file
<?php
//send.php
foreach ($id_letter as $id)
{
$outputText = '<img src="/img/loader.gif" class="img-fluid" alt=""> <p class="center mt-2">Close the window, the send continues automatically</p>';
closeOutput($outputText);
processmail($con,$id);
}
?>
The send.php is call from a AJAX
<script>
$(document).ready(function()
{
$("#btnSend").click(function(e)
{
e.preventDefault();
var dataform= $("#MyCheckForm").serialize();
$.ajax( {
url: "send.php?id=<?=$id?>",
method: "POST",
data: dataform,
success: function(data) {
console.log(data);
$("#myModal").modal("show");
$(".modal-title").html('Sending in progress');
$("#add_body").html(data);
},
error: function(data) {
console.log(data);
}
});
});
});
</script>
many thanks to all
I'm working on a one-way messaging system using server-sent events. I have a file (server.html) which sends the contents of a textarea to a PHP file (handler.php).
function sendSubtitle(val) {
var xhr = new XMLHttpRequest();
var url = "handler.php";
var postdata = "s=" + val;
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(postdata);
//alert(val);
}
This works (alert(val) displays the text in the textarea).
My handler.php code looks like this:
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$stringData = $_POST['s'];
echo "data: Data is {$stringData}\n\n";
flush();
And the relevant part of my SSE receiver file (client.html) is as follows:
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("handler.php");
source.onmessage = function(event) {
var textarea = document.getElementById('subtitles');
textarea.value += event.data + "<br>";
textarea.scrollTop = textarea.scrollHeight;
};
} else {
document.getElementById("subtitles").value = "Server-sent events not supported.";
}
The problem is that client.html only displays "data: Data is", so the text from server.html is getting lost somewhere along the way. I imagine it's the PHP code that's falling over, but I can't work out what's wrong. If anyone can help, I'd appreciate it.
EDIT
I chose to use SSE as opposed to websockets as I only need one-way communication: server.html should push the contents of its textarea to client.html whenever it changes. All the examples of SSE that I've looked at (and I've looked at a lot!) send "automatic" time-based data. I haven't seen any that use real-time user input. So perhaps I should clarify my original question and ask, "How can I use SSE to update a DIV (or whatever) in web page B whenever the user types in a textarea in web page A?"
UPDATE
I've narrowed the issue down to the while loop in the PHP file and have therefore asked a new question: Server-side PHP event page not loading when using while loop
Assuming you want to send a value from server.html and a value at client.html will be automatically updated...
You will need to store the new value somewhere because multiple instances of a script do not share variables just like that. This new value can be stored in a file, database or as a session variable, etc.
Steps:
Send new value to phpScript1 with clientScript1.
Store new value with phpScript1.
Connect clientScript2 to phpScript2.
Send stored value to clientScript2 if it is changed.
Getting the new value 'on the fly' means phpScript2 must loop execution and send a message to clientScript2 whenever the value has been changed by clientScript1.
Of course there are more and different approaches to achieve the same results.
Below there's some code from a scratchpad I've used in previous project.
Most parts come from a class (which is in development) so I had to adopt quite a lot of code. Also I've tried to fit it into your existing code.
Hopefully I didn't introduce any errors.
Do note I did not take any validation of your value into account! Also the code isn't debugged or optimized, so it's not ready for production.
Client side (send new value, e.g. your code):
function sendSubtitle(val) {
var xhr = new XMLHttpRequest();
var url = "handler.php";
var postdata = "s=" + val;
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(postdata);
//alert(val);
}
Server side (store new value):
<?php
session_start();
$_SESSION['s'] = $_POST['s'];
Client side (get new value):
//Check for SSE support at client side.
if (!!window.EventSource) {
var es = new EventSource("SSE_server.php");
} else {
console.log("SSE is not supported by your client");
//You could fallback on XHR requests.
}
//Define eventhandler for opening connection.
es.addEventListener('open', function(e) {
console.log("Connection opened!");
}, false);
//Define evenhandler for failing SSE request.
es.addEventListener('error', function(event) {
/*
* readyState defines the connection status:
* 0 = CONNECTING: Connecting
* 1 = OPEN: Open
* 2 = CLOSED: Closed
*/
if (es.readyState == EventSource.CLOSED) {
// Connection was closed.
} else {
es.close(); //Close to prevent a reconnection.
console.log("EventSource failed.");
}
});
//Define evenhandler for any response recieved.
es.addEventListener('message', function(event) {
console.log('Response recieved: ' + event.data);
}, false);
// Or define a listener for named event: event1
es.addEventListener('event1', function(event) {
var response = JSON.parse(event.data);
var textarea = document.getElementById("subtitles");
textarea.value += response + "<br>";
textarea.scrollTop = textarea.scrollHeight;
});
Server side (send new value):
<?php
$id = 0;
$event = 'event1';
$oldValue = null;
session_start();
//Validate the clients request headers.
if (headers_sent($file, $line)) {
header("HTTP/1.1 400 Bad Request");
exit('Headers already sent in %s at line %d, cannot send data to client correctly.');
}
if (isset($_SERVER['HTTP_ACCEPT']) && $_SERVER['HTTP_ACCEPT'] != 'text/event-stream') {
header("HTTP/1.1 400 Bad Request");
exit('The client does not accept the correct response format.');
}
//Disable time limit
#set_time_limit(0);
//Initialize the output buffer
if(function_exists('apache_setenv')){
#apache_setenv('no-gzip', 1);
}
#ini_set('zlib.output_compression', 0);
#ini_set('implicit_flush', 1);
while (ob_get_level() != 0) {
ob_end_flush();
}
ob_implicit_flush(1);
ob_start();
//Send the proper headers
header('Content-Type: text/event-stream; charset=UTF-8');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no'); // Disables FastCGI Buffering on Nginx
//Record start time
$start = time();
//Keep the script running
while(true){
if((time() - $start) % 300 == 0){
//Send a random message every 300ms to keep the connection alive.
echo ': ' . sha1( mt_rand() ) . "\n\n";
}
//If a new value hasn't been sent yet, set it to default.
session_start();
if (!array_key_exists('s', $_SESSION)) {
$_SESSION['s'] = null;
}
//Check if value has been changed.
if ($oldValue !== $_SESSION['s']) {
//Value is changed
$oldValue = $_SESSION['s'];
echo 'id: ' . $id++ . PHP_EOL; //Id of message
echo 'event: ' . $event . PHP_EOL; //Event Name to trigger the client side eventhandler
echo 'retry: 5000' . PHP_EOL; //Define custom reconnection time. (Default to 3000ms when not specified)
echo 'data: ' . json_encode($_SESSION['s']) . PHP_EOL; //Data to send to client side eventhandler
//Note: When sending html, you might need to encode with flags: JSON_HEX_QUOT | JSON_HEX_TAG
echo PHP_EOL;
//Send Data in the output buffer buffer to client.
#ob_flush();
#flush();
}
//Close session to release the lock
session_write_close();
if ( connection_aborted() ) {
//Connection is aborted at client side.
break;
}
if((time() - $start) > 600) {
//break if the time exceeds the limit of 600ms.
//Client will retry to open the connection and start this script again.
//The limit should be larger than the time needed by the script for a single loop.
break;
}
//Sleep for reducing processor load.
usleep(500000);
}
You called handler.php first time in the server.html and again in client.html. Both are different processes. The variable state won't be retained in the web server. You need to store it somewhere if you want that value in another PHP process. May be you can use sessions or database.
While using sessions you can store the values in two files like:
<?php
//server.php
session_start();
$_SESSION['s'] = $_POST['s'];
And in client.php
<?php
//client.php
session_start();
echo "data: Data is ".$_SESSION['s']."\n\n";
I have this little problem with downloading my xlsx-file.
I am sending my request for the file over jquery Ajax and on the backend the data is correctly collected and assembled to a xlsx-file. So now on its way back to the frontend i am setting all the headers in preparation to force download the file, but the download never starts.
These are the response headers of my request:
Connection Keep-Alive
Content-Disposition attachment; filename="export.xlsx"
Content-Length 346420
Content-Type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Date Mon, 23 Nov 2015 13:23:30 GMT
Keep-Alive timeout=5, max=91
Server Apache/2.4.16 (Win32) OpenSSL/1.0.1p PHP/5.6.12
Set-Cookie <cookiesettings>
content-transfer-encoding binary
x-powered-by PHP/5.6.12
imho the download should start immediately, but nothing happens.
EDIT:
Until now I used a form submit, but the data amount is really big so the time which is needed to assemble the file is also really long and sometimes a couple of minutes or even an hour, so this was no longer possible.
So I built a java-job to build the file and startet an ajax snippet which asks for completion every second or so.
So here is my Code.
Frontend:
This is called on button-click
download: function (type, maximum) {
var
self = this,
oParams = this.oTable.oApi._fnAjaxParameters(this.oTable.fnSettings()),
aoPost = [
{ 'name': 'exportType', 'value': type },
{ 'name': 'exportMax', 'value': maximum },
{ 'name': 'handleId', 'value': self.options.handleId }
],
nIFrame, nContentWindow, nForm, nInput, i
;
// Call a self made function to get extra search parameters
// without call an data update AJAX call.
self.oTable.fnSettings().addAdditionalSearchData(oParams);
// Create an IFrame to do the request
nIFrame = document.createElement('iframe');
nIFrame.setAttribute('id', 'RemotingIFrame');
nIFrame.style.border = '0px';
nIFrame.style.width = '0px';
nIFrame.style.height = '0px';
document.body.appendChild(nIFrame);
nContentWindow = nIFrame.contentWindow;
nContentWindow.document.open();
nContentWindow.document.close();
nForm = nContentWindow.document.createElement('form');
nForm.className = 'export-table';
nForm.setAttribute('method', 'post');
// Add POST data.
var formData = {};
for (i = 0; i < aoPost.length; i++) {
nInput = nContentWindow.document.createElement('input');
nInput.setAttribute('name', aoPost[ i ].name);
nInput.setAttribute('type', 'text');
nInput.value = aoPost[ i ].value;
nForm.appendChild(nInput);
formData[aoPost[ i ].name] = aoPost[ i ].value;
}
// Add dataTables POST.
for (i = 0; i < oParams.length; i++) {
nInput = nContentWindow.document.createElement('input');
nInput.setAttribute('name', oParams[ i ].name);
nInput.setAttribute('type', 'text');
nInput.value = oParams[ i ].value;
nForm.appendChild(nInput);
formData[oParams[ i ].name] = oParams[ i ].value;
}
nForm.setAttribute('action', '/service/exportTableData');
// Add the form and the iFrame.
nContentWindow.document.body.appendChild(nForm);
// Send the request.
//nForm.submit();
// Send the request.
var form = $(nContentWindow.document.body).find('form.export-table');
var jobId = 0;
form.ajaxForm(
{
'showMessagesOnSuccess': false
},
{
'getData': function () {
return formData;
}
}
).data('ajaxForm').submit();
}
The Ajax request on submit:
$.ajax({
type: 'POST',
url: self.handler.getServiceUrl(),
timeout: GLOBALS.AJAX_REQUEST_TIMEOUT,
cache: false,
data: (<get the Data>)
,
success: function (response) {
if (response.success === true) {
// Check if we have to wait for a result.
if (response.jobId !== undefined && response.jobId !== 0) {
self.checkJobStatus(response.jobId);
} else {
<success - show some messages>
}
} else {
self.handler.error(response);
}
},
error: function () {
<Show error Message>
}
});
The CheckJobStatus:
checkJobStatus: function (jobId) {
var self = this;
$.ajax({
type: 'POST',
timeout: GLOBALS.AJAX_REQUEST_TIMEOUT,
cache: false,
data: { 'jobId': jobId },
url: self.handler.getServiceUrl(),
success: function (response) {
if(response !== null && response.data !== undefined) {
if (response.data.isFinished === true) {
if (response.success === true) {
// Check if we have to wait for a result.
self.handler.success(response);
} else {
self.handler.error(response);
}
} else if (response.success === true && response.data !== null) {
setTimeout(
function () {
self.checkJobStatus(jobId);
},
500
);
} else {
Helper.logFrontendError();
}
} else if (response !== null && response.success === true) {
setTimeout(
function () {
self.checkJobStatus(jobId);
},
1000
);
} else {
Helper.logFrontendError();
}
},
error: function (response) {
Helper.logFrontendError();
}
});
}
Backend - php:
(...)
if ($action == 'exportTableData' || $action == 'exportChartData') {
$responseData = $service->execute();
if(isset($responseData->data['contentType']) && $responseData->data['contentType'] != null && isset($responseData->data['data'])) {
$this->sendTextData($responseData->data['contentType'], $responseData->data['data']);
} else {
$this->sendJsonData($responseData);
}
} else {
$this->sendJsonData($service->execute());
}
(...)
private function sendTextData($contentType, $data) {
$this->set('filename', 'export.xlsx');
$this->set('data', $data);
$this->response->type($contentType);
$this->render('/Layouts/excel', 'excel');
}
(...)
$handlerResult = new HandlerResult();
if($dataServiceResult == null) {
$service = new DataService();
$dataServiceResult = $service->exportTableData(
$controller->Auth->User('id'),
json_encode($request->data),
null
);
} else {
if ($dataServiceResult->header->resultKey == 0) {
$handlerResult->wsData['data'] = $dataServiceResult->data;
$handlerResult->wsData['contentType'] = $dataServiceResult->contentType;
}
}
$handlerResult->wsResultHeader = $dataServiceResult->header;
return $handlerResult; // ++++ this result returns to the first codeblock in this section ++++
Backend - java - This is where the File is assembled:
(...)
if (jobId > 0) {
FrontendJobStatus status = FrontendJobQueue.getJobStatus(context.userId, jobId);
this.result = (WSExportTableDataResult) status.getResult();
logger.info((this.result.data == null) ? "ByteArray is EMPTY" : "ByteArray is NOT EMPTY");
} else {
this.jobId = FrontendJobQueue.addJob(this.context.userId, new ExportTableDataJob(this.context, this.postData));
this.result.header.jobId = this.jobId;
}
(...)
The Jop:
<Workbook assembly>
ByteArrayOutputStream out = new ByteArrayOutputStream();
wb.write(out);
this.result.data = out.toByteArray();
this.result.contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
// this.result.contentType = "application/vnd.ms-excel";
this.result.setResultHeader(APIConstants.RESULT_SUCCESS);
Layout/excel:
<?php
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-Transfer-Encoding: binary');
ob_clean();
echo $data;
EDIT 2:
So I tried to open a new window on success with the Data, and i could start the download, but the file ist no valid xlsx File anymore.
var reader = new FileReader();
var blob = new Blob([response.responseText], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
reader.readAsDataURL(blob);
reader.onloadend = function (e) {
window.open(reader.result, 'Excel', 'width=20,height=10,toolbar=0,menubar=0,scrollbars=no', '_blank');
}
Any Ideas?
After a lot of research i found this site and the essence of its statment is that jquery ajax does not support receiving binary data, but provides a solution for implementing plain xhr request which support blob transfer.
The Site:
http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
To expand on my comment, instead of trying to send back binary data via ajax, simply save to a temp file , send the file reference back to js. On receiving the file reference, simply set window.location.href to point to a filereading endpoint, passing the file reference. I have done this a few times and it works fine even on ancient browsers:
$('#start').click(function(){
$.post('/createfile.php', {some:data}, function(response){
if(response.started){
pollFile(response.fileId);
}
});
);
function pollFile(fileId){
$.get('/filestatus.php?fileid=' + fileId, function(response){
if(response.fileCompleted){
window.location.href = '/downloadfile.php?fileid=' + fileId;
}else{
setTimeout('pollFile', 5000, fileId);
}
});
}
//createfile.php
$fileId = uniqid();
SomePersistentStorage::addJob($fileID);
//start file job here, code should run in a seperate process/thread, so
//either use a job queue system, use shell_exec or make an http request,
//then once job is queued/started:
header('Content-Type: application/json');
echo json_encode(['started'=>true, 'fileId'=>$fileId]);
//processjob.php - the file that does the work, could be your java
//program for example, just using php here for consistency
//after file is done
file_put_contents($filepath, $data);
SomePersistentStorage::updateJob($fileID, true);
//filestatus.php
$fileId = $_GET['fileid'];
header('Content-Type: application/json');
echo json_encode(['fileCompleted'=>SomePersistentStorage::isJobCompleted($fileID)]);
//downloadfile.php
$fileId = $_GET['fileid'];
$filepath = 'tmp/' . $fileId . '.tmp';
//correct headers here, then
readfile($filepath);
unlink($filepath);
If you dont want to imediatly delete the file, then you could just run a cron to delete files in the specific folder, that are older than x.
I know there are already many questions about forcing a download with PHP, but I can't find what I'm doing wrong and what should I do.
I'm having an list with filenames, and I want to download one of them by clicking a button.
My jQuery:
$(".MappeDownload").on("click",function(e){
e.stopPropagation();
fileId=$(this).val()
$.post("ajax/DownloadFile.php",{ id : fileId})
})
and on the server side I have a table with the file names and the file path.
$sql = "SELECT vUploadPfad, vUploadOriginname FROM tabUpload WHERE zUploadId='$_POST[id]'";
$result = mysql_query($sql) or die("");
$file = mysql_fetch_array($result);
$localfile = $file["vUploadPfad"];
$name=$file["vUploadOriginname"];
$fp = fopen($localfile, 'rb');
header("Cache-Control: ");
header("Pragma: ");
header("Content-Type: application/octet-stream");
header("Content-Length: " . filesize($localfile));
header("Content-Disposition: attachment; filename='".$name."';");
header("Content-Transfer-Encoding: binary\n");
fpassthru($fp);
exit;
The AJAX request is successful, I'm getting the right header(filesize, filename etc...) but the download are not starting.
You don't need ajax, just redirect to the address that forces the download. The page will not change so, instead of $.post("ajax/DownloadFile.php",{ id : fileId}) you should have location.href = "ajax/DownloadFile.php?id="+fileId and, in your PHP file, convert your $_POST to $_GET
The response to an AJAX request will never trigger a download. AJAX requests are silently handled in the background, they never trigger visible activity directly.
You need to redirect the main page or an iframe to trigger the download.
Return file name in ajax
Do window.location.href = 'returned file name' and download will start!
There is my solution:
<script>
//downloading the file
$(document).on('click', '.download_file', function(){
var path = $(this).data("name");
var action = "download_file"
$.ajax({
url: "action.php",
method: "POST",
data: {path:path, action:action},
success: function(data)
{
window.location.href = path;
}
})
})
</script>
And the action.php
<button type"button" name="download" data-name="'.$name.'" class="download_file btn btn-success btn-xs">Pobierz</button></td>'
I am trying to call a PHP function using AJAX. Below is the script I used.
<script type="text/javascript" src="jquery.1.4.2.js">
$(document).ready(function () {
// after EDIT according to
// #thecodeparadox answer
$('#local').click(function(e){
e.preventDefault();
e.stopPropagation();
promptdownload();
});
});
function promptdownload(e)
{
$.ajax({
type: "POST",
url: "js/prompt.php",
data: { "get" : "runfunction", "action" : "promptlocal" },
success: function (response) {
}
});
}
</script>
The corresponding PHP code (prompt.php) is:
<?php
$path1 = "downloads/1.jpg";
$browserFilename1 = "Local Travel";
$mimeType1 = "image/jpeg";
function promptToDownload($path, $browserFilename, $mimeType)
{
if (!file_exists($path) || !is_readable($path)) {
return null;
}
header("Content-Type: " . $mimeType);
header("Content-Disposition: attachment; filename=\"$browserFilename\"");
header('Expires: ' . gmdate('D, d M Y H:i:s', gmmktime() - 3600) . ' GMT');
header("Content-Length: " . filesize($path));
// If you wish you can add some code here to track or log the download
// Special headers for IE 6
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
$fp = fopen($path, "r");
fpassthru($fp);
}
if ($_POST["action"] = 'promptlocal')
{
promptToDownload($_GET[$path1], $browserFilename1, $mimeType1);//comments
}
?>
This is how I code the button that is supposed to trigger the function:
<input type="button" id="local" name="local" value="Local Travel">
My expected output is to have this button promt the user: "where to save 1.jpg file".
However I couldn't make it work.
Any advise is highly appreciated.
$('local').click(function(e){
should be
$('#local').click(function(e){
As local is an id so you should use # before it. And also in your php code there are some missing quotes.
Use Firebug(FF), Dragonfly(Opera), Developer Tools(Chrome). You can see all javascript errors, warnings and exceptions, and can see ajax requests data.
data: { "get" : "runfunction", "action" : "promptlocal" },
Try to remove the quotes from "get" and "action".
Like this :
data: { get : "runfunction", action : "promptlocal" },
It looks to me like you are trying to download a file with jquery/ajax. You will not get this to work with only ajax. This question has been answered several times on stackoverflow.
I hope this link will help you: Ajax File Download using Jquery, PHP