I have a php script which opens a csv file, edits it, then saves it using 'file_put_contents'. The saved location is the same server directory which contains the script.
file_put_contents($destination,$string);
However, I want the user to have control over where the file is saved on their computer (ie. locally.) Preferably, using a dialogue pop-up on their browser.
How would this be achieved? Do I need to add header information to the php script? Thanks in advance.
To save file in local with popup of window you can use JS (Blob Object) for it :
// ajax get data from server ( you can push file to server for editting it and return)
// data return of php : return stream_get_contents($file);
$.ajax({
type: "post",
url: url,
data: {...},
success: function (data) {
let blob = new Blob([data], { type: 'application/csv;charset=utf-8;' });
handleSaveCsv(blob);
}
});
async function handleSaveCsv(blob){
if( window.showSaveFilePicker ) {
const handle = await showSaveFilePicker({
suggestedName: 'csvExportName.csv',
types: [{
description: 'csv file',
accept: {'application/csv': ['.csv']},
}],
});
const writable = await handle.createWritable();
await writable.write( blob );
writable.close();
}
else {
const saveCsv = document.createElement( "a" );
saveCsv.href = URL.createObjectURL( blob );
saveCsv.download= 'csvExportName.csv';
saveCsv.click();
setTimeout(() => URL.revokeObjectURL( saveCsv.href ), 6000 );
}
}
window.showSaveFilePicker for opening popup window
Related
I would like to download a pdf from my external URL, store it in my server and download it when the button was triggered by click by the user.
I have tried to store it to my server and its success, but then I don't know how to automatically download the pdf as the user wants after they click the button.
My view:
<button id=\"appPrintbutton\" name=\"appPrintbutton\" style=\"cursor:pointer;padding: 3px; margin: 2px;float:left;\" title=\"Cetak Dokumen\"><i class=\"fas fa-check fa-lg fa-fw\"></i>Cetak</button>
<script type="text/javascript">
$("#appPrintbutton").on('click', function() {
var selectedId=[];
selectedId.push(document.getElementById("txtNO_DOK").value);
$.ajax({
url: "<?php echo base_url().'index.php/finance/M_approve/DOWNLOAD_FILE_NILAI'?>",
type: 'POST',
data: {json: JSON.stringify(selectedId)},
dataType: 'json',
success: function (data) {
window.location.href = "<?php echo base_url().'index.php/finance/M_approve/';?>";
},
error: function (data) {
console.log('error');
}
});
return false;
});
</script>
And my Controller was:
public function DOWNLOAD_FILE_NILAI()
{
$msg="";
$status="";
$rptName="";
$pathdir="/wwwroot/php/download/";
$ieselon="ALL";
$data=$this->input->post('json');
$nodok=substr($data, 1, 9);
if($nodok!="" )
{
$randfname = Date("Y_m_d");
$fname = $nodok."_nilai".$randfname.".pdf";
$rptName="\wfinance\kas_cetak.rpt";
$strParamName= "&promptex-no_dok=".$nodok;
$strParamName.= "&promptex-terbilang=".$nodok;
$exportType="PDF";
$serverLink = "http://11.5.1.44:12000/ReCrystallizeServer/ViewReport.aspx?report=".$rptName;
$fullLink=$serverLink.$strParamName."&exportfmt=$exportType";
$fdata = file_get_contents($fullLink);
$fSaveAs=fopen($pathdir."$fname","w");
fwrite($fSaveAs, $fdata);
fclose($fSaveAs);
$status="OK";
}
$dataStatus[] = array(
'Status'=>$status,
'url'=>base_url(),
'fname'=>$fname,
'Msg'=>$msg
);
print_r(json_encode($dataStatus,JSON_PRETTY_PRINT));
}
My desired output:
Automatically download the pdf
Is it any way to do that?
You can use file_put_contents() function in PHP to read and write remote files to the server's directory.
$remote_file = 'http://example.com/path/to/pdffile.pdf';
$local_file = '/documents/new_file.pdf';
file_put_contents($local_file, file_get_contents($remote_file));
Update
If you are unable to write file directly, you can use two step way. First create blank file and after that write to it.
$local_file = fopen("/documents/new_file.pdf", "w")
This will create a file so other functions could write to it.
this way you can display files either from external/internal URL. And in case of downloading, you can provide local URL of /documents/new_file.pdf
So when the user clicks button you can hit ajax which triggers a server-side script which executes the code above.
Make sure your local directory /documents/ is writable by PHP process.
Read: https://www.php.net/manual/en/function.file-put-contents.php
I have an AJAX call to the create_pdf.php page:
$('body').on('click', '.PrintButtonWithClass', function (event) {
var1 = $('#id1').val();
var2 = $('#id2').val();
dataString='var1='+var1+'&var2='+var2+'&pdf_name=PdfName&pdf_creator=myname';
$.ajax({
type: 'post',
url: '/path/to/createpdf/file/create_pdf.php',
data: dataString,
success: function (data) {
alert('success');
}
});
});
In create_pdf.php I tried to use this line to download the file:
$pdf->Output(str_replace(' ','_',utf8_decode($_POST['pdf_name'])).'.pdf', 'D');
I tried also the FD and I parameters with no success, the file does not get downloaded.
How can I force downloading the file created without saving it to the webserver and without redirecting user to any other page? I want him to stay on the same page, and that the browser pops up a (download or preview dialog box) for the PDF. Is there any way to do it?
EDIT : create_pdf.php is Waiting for POST variables. and uses them to create the HMTL for the pdf.
You can try to submit the form to a new window( like a popup ):
<form method="post" id="myform" action="your_url">
<input name="param1">
</form>
And in javascript
// create popup window
var wind = window.open('about:blank', '__foo', 'width=700,height=500,status=yes,resizable=yes,scrollbars=yes');
// submit form to popup window
$("#myform").attr("target", "__foo");
Do not forget to send content-type header from php:
header("Content-Type", "application/pdf");
Edit:
Browsers should display your pdf content and also show download or print options.
The code is not tested but I think it would do what you requested;
I found a work-around for my problem.
I did an AJAX call inside another AJAX call.
the first AJAX call creates the file on webServer and opens the file in a new Window.
In his success parameter I do the following:
The second AJAX call that deletes the file from Server.
$.ajax({
type: 'post',
url: '/path/to/create_pdf.php',
data: dataString,
success: function (data) {
window.open(
data,
'_blank' // <- This is what makes it open in a new window.
);
window.setTimeout(function () {
dataString2 = 'Downloaded=true';
$.ajax({
type: 'post',
url: '/path/to/create_pdf.php',
data: dataString2,
success: function (data) { alert(data); }, // handler if second request succeeds
});
}, 5000);
},
});
Using this answer to my similar request : send a csv file from php to browser
I needed to (1) get and display a pdf in another window; and
(2) get a CSV file and prompt for saving.
I have 2 simple buttons on the page (http://potoococha.net/) for each. Here is the code:
function getCSVText(evt) {
if (currentChecklistCountry) {
var form = $('<form method="post" action="../php/sendCSV.php?country=' + currentChecklistCountry + '"></form>');
$('body').append(form);
form.submit();
form.remove();
}
else checklistCountryButton.classList.add("needsAttention");
}
function openChecklistPage() {
if (!currentChecklistCountry) {
checklistCountryButton.innerHTML = "Select Country";
checklistCountryButton.classList.add("needsAttention");
return;
}
if (gNumDays == undefined) gNumDays = 12;
vars = "?country=" + currentChecklistCountry;
vars += "&num_days=" + gNumDays;
vars += "&line_nos=" + lineNumbers.checked;
vars += "&left_check=" + leftCheck.checked;
vars += "&endemics=" + showEndemics.checked;
vars += "&sci_names=" + !sciNames.checked;
vars += "&italics=" + !italics.checked;
window.open( '../php/makePDF.php' + vars, '_blank' );
}
So the getCSVText() methods downloads a file using a temporary form appended and then immediately removed, and openChecklistPage() successfully opens another browser window with a pdf file. The pdf file is never saved on the server. The CSV file is already stored there and just retrieved. Perhaps you can modify the code for your purposes.
I'm trying to write a method in a php class that will use ajax to execute a php function that will push a file back to the browser.
It seems like its trying to write the file to the modx log, getting a lot of binary garbage in there.
Here is the method:
public function pushDocuments($formdata){
$data = $formdata['formdata'];
$file = MODX_PROTECTED_STORAGE . $data['target'];
$file_name = basename($file);
if (file_exists($file)) {
header("Content-Disposition: attachment; filename=\"$file_name\"");
header("Content-Length: " . filesize($file));
header("Content-Type: application/octet-stream;");
readfile($file);
};
$output = array(
'status' => 'success',
'error_messages' => array(),
'success_messages' => array(),
);
$output = $this->modx->toJSON($output);
return $output;
}
and here is the jquery:
$('.btn-get-document').click(function(){
var target = $(this).attr('data-target');
var postdata = {"snippet":"DataSync", "function":"pushDocuments", "target": target}; // data object ~ not json!!
console.log('target = ' + target + postdata );
$.ajax({
type: "POST",
url: "processors/processor.ajax.generic/",
dataType : "json",
cache : false,
data: postdata, // posting object, not json
success: function(data){
if(data.status == 'success'){
console.log("SUCCESS status posting data");
}else if(data.status == 'error'){
console.log("error status posting data");
}
},
error: function(data){
console.log("FATAL: error posting data");
}
});
});
it's running through the scripts and giving a success in the console [because I am forcing success] but no file is prompted for download and the binary garbage shows up in the modx log
What am I doing wrong?
In order to download a file, you'd have to use JS to redirect to the file's location. You can't pull the file contents through AJAX and direct the browser to save those contents as a file.
You would need to structurally change your setup. For instance, your PHP script can verify the existence of the file to be downloaded, then send a link to JS in order to download the file. Something like this:
if ( file_exists( $file )) {
$success_message = array(
'file_url' => 'http://example.com/file/to/download.zip'
);
}
$output = array(
'status' => 'success',
'error_messages' => array(),
'success_messages' => $success_message
);
Then modify the "success" portion of your AJAX return like this:
success: function( data ) {
if ( data.status == 'success' ) {
location.href = data.success_messages.file_url;
} else if ( data.status == 'error' ) {
console.log( 'error status posting data' );
}
},
Since you're directing to a file, the browser window won't actually go anywhere, so long as the file's content-disposition is set to attachment. Typically this would happen if you directed to any file the browser didn't internally handle (like a ZIP file). If you want control over this so that it downloads all files (including things the browser may handle with plugins), you can direct to another PHP script that would send the appropriate headers and then send the file (similar to the way you're sending the headers and using readfile() in your example).
#sean-kimball,
You might want to extend MODX's class based processor instead:
https://github.com/modxcms/revolution/blob/master/core/model/modx/processors/browser/file/download.class.php
It does the download from any media source and also access checking if you want.
Its implementation on manager side is:
https://github.com/modxcms/revolution/blob/master/manager/assets/modext/widgets/system/modx.tree.directory.js#L553
Back to your case, these examples might bring you some ideas.
JS Example:
$.ajax({
type: "POST",
// read my note down below about connector file
url: "assets/components/mypackage/connectors/web.php",
dataType : "json",
cache : false,
data: {
action: 'mypath/to/processor/classfile'
}
success: function(data){
},
error: function(data){
console.log("FATAL: error posting data");
}
});
Processor example:
<?php
require_once MODX_CORE_PATH . 'model/modx/processors/browser/file/download.class.php';
class myDownloadProcessor extends modBrowserFileDownloadProcessor {
// override things in here
}
return 'myDownloadProcessor';
For this, I also suggest you to use MODX's index.php main file as the AJAX's connector so the $modx object in processor inherits the access permission as well.
http://www.virtudraft.com/blog/ajaxs-connector-file-using-modxs-main-index.php.html
I'm using the Laravel framework. I have a form of adding a new item to the database and in that form the user can also drag and drop a file. Then, a progress bar is displayed until it's completed, using Ajax for uploading the file to the server.
Once submitting that form, I run the addItem function in a controller and I want to do/check:
That the file is already hosted in the server (successful upload)
If the file is hosted in the server, how do I find it? (I gave it a random name)
If the user chose not to submit the form, I wish to erase that file from the server, so I won't have files that are not connected to any item on my database
Can you suggest any ideas on how to complete these tasks?
To send files by AJAX you need to use FormData which is a class of XMLHttpRequest2, it doesn't work with IE<10.
You also need AJAX2 to show progress.
SAMPLE SUBMIT FORM WITH FILES AND PROGRESS VIA AJAX:
Here I have made an example. In this example the form sends the data and files via AJAX using FormData and show the upload progress percentage in #progress using the progress event. Obviously it is a sample and it could be changed to adapt it.
$('form').submit(function(e) { // capture submit
e.preventDefault();
var fd = new FormData(this); // XXX: Neex AJAX2
// You could show a loading image for example...
$.ajax({
url: $(this).attr('action'),
xhr: function() { // custom xhr (is the best)
var xhr = new XMLHttpRequest();
var total = 0;
// Get the total size of files
$.each(document.getElementById('files').files, function(i, file) {
total += file.size;
});
// Called when upload progress changes. xhr2
xhr.upload.addEventListener("progress", function(evt) {
// show progress like example
var loaded = (evt.loaded / total).toFixed(2)*100; // percent
$('#progress').text('Uploading... ' + loaded + '%' );
}, false);
return xhr;
},
type: 'post',
processData: false,
contentType: false,
data: fd,
success: function(data) {
// do something...
alert('uploaded');
}
});
});
See how works!!: http://jsfiddle.net/0xnqy7du/3/
LARAVEL:
In laravel you can get the file with Input::file, move to another location and save in the database if you need it:
Input::file('photo')->move($destinationPath, $fileName);
// Sample save in the database
$image = new Image();
$image->path = $destinationPath . $fileName;
$image->name = 'Webpage logo';
$image->save();
I am trying to make an image upload where the JavaScript posts DataURI of an image via AJAX and the PHP receives it to decode it into an image.
The problem is, everything is working fine except that the end product is not an image file.
Please have a look at the following example code.
JavaScript:
dataString='encodedimg='+e.target.result.match(/,(.*)$/)[1]+'&type='+type;
$.ajax({
url: 'uploadhandler_ajax.php',
type: 'POST',
data: dataString,
success: function(data){
//print success message
});
PHP:
$encodedimg = $_POST['encodedimg'];
file_put_contents('asdf.png', base64_decode($encodedimg));
There is no problem with $_POST['encodedimg'] as it produces the right image using online base64 converter. So I am assuming that there is a misuse with file_put_contents() or base64_decode().
Appreciate the help!
To read image on PHP i used a function like this
function rcd($data) {
$p = strpos($data, ',');
$d = base64_decode(substr($data, $p+1));
$rfn = md5(mt_rand(1,123123123));
file_put_contents($rfn, $d, LOCK_EX);
return $rfn;
}
Usage example:
$img_file_name = rcd($_POST['image_data']);
On JS part it is tricky (different browsers, etc). First of all You need to have the image data. Now You do not precise how this is sourced and the code example does not give a hint. We can assume some options
Simple You get dataString properly populated by whatever means neccesary, then Your example should basically work
imgdata = .... // any means of getting the data
$.ajax({
url: 'uploadhandler_ajax.php',
type: 'POST',
image_data: imgdata,
success: function(data){
//print success message
});
Not so simple You have a Canvas object on the screen which was populated by any means and You want to send that data. Whatever above is true, however the way to get image data would be
var canv = document.getElementById('id_of_canvas');
imgdata = canv. toDataURL('image/jpeg', 0.88); // last arg is quality
However, as some browsers (mobiles) might not be so lucky to support this, you might want to find JPEGEncoder for JS and add it, along with the code below, to Your project.
var tdu = HTMLCanvasElement.prototype.toDataURL;
HTMLCanvasElement.prototype.toDataURL = function(type,param1)
{
var res = tdu.apply(this,arguments);
if(res.substr(0,11) != "data:image/")
{
var encoder = new JPEGEncoder();
return encoder.encode(this.getContext("2d").getImageData(0,0,this.width,this.height), (param1 ? param1*100 : 88));
}
else return res;
}
Hope this helps!
FOr #Marcin Gałczyński:
$.ajax({
url: 'uploadhandler_ajax.php',
type: 'POST',
image_data: imgdata,
success: function(data){
//print success message
}
})
I think jQuery.ajax didnt have image_data jQuery.ajax