Edit: I have a solution of sorts, but I do not understand why ajax is not working. Please see the edit below.
I want to save a txt file generated in javascript to the client machine.
I want the user to remain on the same webpage.
I am doing it via ajax with a call to a php page.
I want it to work in all major pc browsers; Chrome, Firefox, Internet Explorer and Edge.
Here is what I have so far:
htmlsave.js
dataARR = {
SaveFile: "This is the content.\nLine2.\nLine3"
};
var dataJSON = JSON.stringify(dataARR);
$.ajax({
type: "POST",
url: "htmlsave.php",
async: true,
cache: false,
crossDomain: false,
data: { myJson: dataJSON },
success: function (data) {
alert("success");
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("failure");
}
});
htmlsave.php
<?php
if(!isset($_POST) || !isset($_POST['myJson'])) {
exit(0);
}
$data = json_decode($_POST['myJson'], true);
if(!array_key_exists('SaveFile', $data) || !isset($data['SaveFile'])){
exit(0);
}
$contents = strval($data['SaveFile']);
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false); // required for certain browsers
header("Content-Type: text/plain");
header("Content-Disposition: attachment; filename=\"mysavefile.txt\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . strlen($contents));
ob_clean();
flush();
echo $contents;
?>
What comes back (ie the data argument in success) is "This is the content.\nLine2.\nLine3".
There is no prompt to save the file with content "This is the content.\nLine2.\nLine3" as I had hoped.
Edit: A solution (but not a great one)
I have done a bit of tinkering and have something that works, in a fashion. Instead of calling through ajax I write in my javascript
document.location = "htmlsave.php?myJson=Line1Line2";
and in my php I have altered it to a get with
if(!isset($_GET) || !isset($_GET['myJson'])) {
exit(0);
}
$contents = $_GET['myJson'];
This works. This will be ok for small files, but I wanted to do reasonably large text files, and even zipped using lzw encoding. Anyone know why Ajax is not working?
Make sure your htmlsave.php starts with
<?php
If it does, see if your webserver is configured to pass .php files to your php implementation (fpm, mod_php, etc...)
Related
I need to export data from a database through a webapp. And when I call the function with ajax post method. The file is created successfully on the server, but after that it is not downloading it, the download function is not called, somehow, or it is not working. I don't know the reason, and I don't get any error messages.
Here is my jQuery function that sets post data for the php:
$(document).ready(function(){
$('#exportCSV').click(function(){
var ajaxurl = 'admin.php?page=site&edit=false',
data = {'export': 'csv'};
$.post(ajaxurl, data, function (response) {
});
});
});
A switch that runs the selected file export function, than tries to download it
switch ($_POST['export']) {
case 'csv':
$query = $_SESSION['query'];
$FName=exportToCSV($conn,$query,$fName);
download($FName);
break;
}
And the download script that is not called, or is not working correctly
function download($file){
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=$file");
header("Content-Length: ".filesize($file));
header("Content-Type: application/csv");
header("Content-Transfer-Encoding: binary");
readfile($file);
}
And this is how this "site" is called.
Everything above is in the "somesite.php" file
if(isset($_GET['page'])){
switch ($_GET['page']) {
case 'site':
include("somesite.php");
break;
}
}
I am using this great jquery.fileDownload.js plugin to allow users to download self-generated CSV files from my website.
From what I understood, based in the PHP code from this site (suggested by fileDownload creator), I need to set a cookie to identify when the download is sucessfull or not:
try {
$page = file_get_contents($filepath);
echo $page;
header('Set-Cookie: fileDownload=true; path=/');
} catch(Exception $e) {
header('Set-Cookie: fileDownload=false; path=/');
}
It works as expected when fileDownload=true, but fail event is never fired when fileDownload=false.
Here is my javascript code:
$.fileDownload('export.php')
.done(function() {
alert('Done!');
})
.fail(function() {
alert('Fail!');
});
And here is my export.php code:
header('Pragma: no-cache');
header('Cache-Control: no-cache, must-revalidate');
header('Content-type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="file.csv"');
try {
// Code for generating the CSV file
header('Set-Cookie: fileDownload=true; path=/');
} catch(Exception $e) {
header('Set-Cookie: fileDownload=false; path=/');
}
exit;
When file is generated correcly I got the success message, but never get the fail message, even when simulating an issue.
Please, what I am missing? Thank you!
It looks like the cookie is only used to check if a download was completed, but not to assess failure. But it does catch HTTP errors, so the solution is simply sending a 500 from the server:
try {
$page = file_get_contents($filepath);
echo $page;
header('Set-Cookie: fileDownload=true; path=/');
} catch(Exception $e) {
http_response_code(500);
}
Here is my code:
PHP:
public function export(Request $request){
$file = "export.txt";
if(isset($_POST["type"])){
file_put_contents("$file",$_POST["text"]);
}
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: text/plain'); // the appropriate header type for txt file
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
}
}
JS:
$(document).on('click', '#export', function () {
var names = ['علی','فرید'];
var namess = names.join('\n');
$.ajax({
type: "post",
url: "/export",
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
data: {
type: "save",
text: namess
},
dataType: "json",
success: function(){
var href = '/export?filename=export.txt';
window.location = href;
},
error: function(){
alert('wrong');
}
});
})
Always error part executes. I mean it always alert wrong. How can I fix it? All I'm trying to do it making a .txt download file.
Noted that when I run this path: http://localhost:8000/export?filename=export.txt .. then export.txt will be downloaded.
You can download using this code:
window.location="export?filename=export.txt";
If you want to post data :
$('<form action="comments.php" method="POST"/>')
.append($('<input type="hidden" name="type" value="save">'))
.append($('<input type="hidden" name="text" value="'+ namess +'">'))
.appendTo($(document.body)) //it has to be added somewhere into the <body>
.submit();
Full code:
$(document).on('click', '#export', function () {
var names = ['علی','فرید'];
var namess = names.join('\n');
$('<form action="export?filename=export.txt" method="POST"/>')
.append($('<input type="hidden" name="type" value="save">'))
.append($('<input type="hidden" name="text" value="'+ namess +'">'))
.appendTo($(document.body)) //it has to be added somewhere into the <body>
.submit();
});
});
Basically Ajax is not usually used for file download however you can tune to make it feel and that is what you have done the only thing is when the request is successful us a "document.location" function from javascript to popup a new window for downloading a file. Besides for the error you are getting, try debugging your PHP code by playing around with your PHP header by starting with most important PHP Headers such as
header('Content-Type: text/plain');
header('Content-Disposition: attachment;filename="'.basename($file).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
ob_clean();
flush();
readfile($fileName);
exit;
check you code works with minimal information if it works then start adding more header one at a time, this helps in resolving problem with minimal information.
Hope this helps.
Scouring the net for how to separate the download data from the success/fail data when doing a download using a standardsubmit in extjs, and can't find anything specific about how to do this.
For example, I have a hidden form that I am using to force a download:
var hiddenForm = Ext.create('Ext.form.FormPanel', {
standardSubmit: true,
height: 0,
width: 0,
hidden: true
});
hiddenForm.getForm().submit({
url: 'downloadtxt.php',
method: 'POST',
success: function(form, action) {
console.log('success');
},
failure: function(form, action) {
console.log('failure');
}
});
and on the server in PHP I have this code:
$file_name = "test.txt";
$file_data = "The text to save in the file";
header("Content-Type: " . "text/plain");
header("Content-Length: " . strlen($file_data));
header("Content-Disposition: attachment; filename=\"" . $file_name . "\"");
$result["success"] = true;
$result["errors"] = array();
$result["data"] = $file_data;
echo json_encode($result);
In this case, the entire contents of the $result array gets put into the download file (effectively buggering the contents, and additionally the success/fail methods never get called in the submit() code. Now, if I change the PHP server code to:
$file_name = "test.txt";
$file_data = "The text to save in the file";
header("Content-Type: " . "text/plain");
header("Content-Length: " . strlen($file_data));
header("Content-Disposition: attachment; filename=\"" . $file_name . "\"");
echo $file_data;
then the downloaded file contains what I expected ("The text to save in the file"), but the success/fail methods still never get called in the submit() code. How is it possible to trigger the success/fail code, but yet not bugger up the contents of the download file?
I've recently made an HTML form to fill some service reports & now want to save & retrieve them on/from PC itself (preferably in xml format) (not on server).
I'hv checked it on w3scools /on this page too but still I couldn't get the desired solution.
Could you please inform me the possible ways to make it happen.
You can examine the Javascript behind this application (http://www.cloudformatter.com/Nimbus) like this page here:
Sample Nimbus Invoice
This code shows (1) data in the editor area being saved to local storage constantly, (2) option to make a pseudo HTML/XML file and download to disk (Save to ... Disk), (3) option to make that same package and save to storage on the web (New Nimble, Save Nimble). I am not going to go through all the code, the most relevant is here below for saving locally to disk a package stored in the variable "archive":
function SaveFile(archive) {
var textToBLOB = new Blob([archive.innerHTML], { type: 'text/xml' });
var sFileName = 'varypackage.html';
var newLink = document.createElement('a');
newLink.download = sFileName;
if (window.webkitURL !== null) {
newLink.href = window.webkitURL.createObjectURL(textToBLOB);
}
else {
newLink.href = window.URL.createObjectURL(textToBLOB);
newLink.style.display = 'none';
document.body.appendChild(newLink);
}
newLink.click();
}
Look here for all the code (which contains much more that just the saving/loading but it is in here): http://www.cloudformatter.com/Resources/pages/varyhtml/script/02_control.js
If I understand you, you want to store the data on the user's computer, is that right?
Then it would be the best to create an XML file and force download to user's computer.
(I'm using jQuery because it's fastest)
STEP 1: make an ajax request.
function downloadFormData(form){
var formData= $(form).serialize(); //form is an DOM element - the form you want to retrieve data from
$.ajax({
url:"save.php",
data: formData,
success: function (file) {
window.location.href = "download.php?path="+ file
}
});
}
2) Create a php file, name it as save.php with the following content in your root directory:
<?php
$xml_string = "<?xml version etc.?><data>";
foreach($_POST as $key=>$value)
$xml_string.='<'.$key.'>'.$value.'</'.$key.'>';
$xml_string.='</data>
$file = 'formData.xml';
// save to file
file_put_contents('./'.$file, $xml_string);
// return the filename
echo $file; exit;
5) Create a php file, name it as download.php with the following content in your root directory
<?php
$file = 'formData.xml'
// $file = trim($_GET['path']); if you use download.php for anything else
// force user to download the file
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/xml');
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);
unlink($file);
exit;
}
else {
echo "$file not found";
}
I hope you understand the way this works, because you definetly can't leave everything like that (then users could download any file they wanted), but i hope my answer will help you.
EDIT:
a PURE javascript solution
function createXMLandDownload(form){
var a = document.createElement("a");
a.download = "MyXml.xml"; //downloaded file name
a.addEventListener("click" , function (){
var xmlstring = '<?xml version="1.0" encoding="utf-8"?><data>';
$(form).find(":input").each(function(){
xmlstring+="<"+this.name+">"+this.value+"</"+this.name+">";
}
xmlstring+="</data>";
document.open('data:Application/octet-stream,' + encodeURIComponent(xmlstring ));
});
a.click();
}