Downloading an Excel File with php - php

I am trying to download an excel file to the client that I save on the server first (for backup purposes). The problem is I get a completely different file server side than I do client side. The server side is correct, the client side lists my sql queries and the time it takes. Note the xlsx is multiple sheets in one workbook and it needs to be that way.
public static function generate()
{
//Override the max execution time because our report takes longer than 30 seconds to run
ini_set("max_execution_time", 300);
$objPHPExcel = new PHPExcel();
$objPHPExcel = ErsReport::generateDemographics($objPHPExcel);
$objPHPExcel = ErsReport::generateWorkLocationTab($objPHPExcel);
$objPHPExcel = ErsReport::generateDisabilityCategoryTab($objPHPExcel);
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
$objWriter->save('ERS Reports\SpeedERS_'.date("Y_m_d").'.xlsx');
//$objWriter->save('SpeedERS.xlsx');
}
public static function download()
{
header ('Content-Type: application/vnd.ms-excel; charset=utf-8');
header ('Content-Disposition: attachment; filename="ERS Reports\SpeedERS_'.date("Y_m_d").'.xlsx"');
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
readfile('ERS Reports\SpeedERS_'.date("Y_m_d").'.xlsx');
}
I call generate then download. The generate shows the correct data.
When I call download it shows improper data and only one page.
EDIT
The function calling generate & download.
function submitERSReport (formLocation) {
{{ErsReport::generate();}}
{{ErsReport::download();}}
}

I called it like this instead.
function submitERSReport () {
$.ajax({
url: "/report/generateERS",
type: "POST",
dataType: "json",
error: function(XMLHttpRequest, textStatus, errorThrown){
alert("Request failed: getting ERS: " + textStatus + " " + errorThrown);
},
success: function(msg){
var iframe = document.createElement("iframe");
iframe.style.display = "none";
iframe.src = msg.filename;
$('body').append(iframe);
}
});
}
With this controller.
public function action_generateERS()
{
$filename = ErsReport::generate();
return json_encode(array("filename"=>$filename));
}
And I made one change to the model.
$objWriter->save($_SERVER['DOCUMENT_ROOT'].'/ERS/SpeedERS_'.date("Y_m_d").'.xlsx');
return('/ERS/SpeedERS_'.date("Y_m_d").'.xlsx');
It works now, but I'm still open to suggestions.

Related

JPG (Binary data) download as AJAX response

I have a page where a user can download an image in the format he ask, the request is sent to a PHP script that produce the image and ... I want to serve to the user.
This is the JQUERY code sourced here on StackOverflow
$('[name ="download_img_ajax"]').click(function(e){
e.preventDefault()
var element = this
var formdata = new FormData(element.closest(".form_downIMG"))
formdata.append('download_img_ajax','true')
$(this).next("span.down_response").html('Preparazione file in corso...'),
$('.emailadr').hide(),
$.ajax({
type: 'POST',
url: '$target_post',
data: formdata,
cache: false,
contentType: false,
processData: false,
success: function(tornato) {
const blob = new Blob([tornato], {type: 'image/jpeg'});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "file.jpg";
document.body.appendChild(a);
a.click();
},
})
})
The PHP script generate the file as a tmp file ($img), but I don't understand how return it as a correct AJAX response.
echo fread($img,filesize($img_path));
isn't working (the file isn't recognized as JPG file) even if the size is correct.
In a normal form I return the file this way:
header('Content-Transfer-Encoding: binary'); // For Gecko browsers mainly
header('Accept-Ranges: bytes'); // For download resume
header('Content-Length: ' . filesize($img_path)); // File size
header('Content-Encoding: none');
header('Content-Type: image/jpeg'); // Change this mime type if the file is not PDF
header('Content-Disposition: attachment; filename=' . $imgID); // Make the browser display the Save As dialog
readfile($img_path);
fclose($img);
PS: In JQ I use next, closest, etc. because I have many forms in the same dynamically generated by PHP.
I will use this kind of download only for file of ~700KB, leaving the bigger file on the old method of a submit form with target="_blank" and readfile in the PHP. Is safe?
If I want manage also error (example the PHP script can't serve the file) how I can handle it?
Thanks.
Solved with FileSaver.js ( https://github.com/eligrey/FileSaver.js )
var xhr = new XMLHttpRequest()
xhr.open('POST', '$target_post')
xhr.responseType = 'blob'
xhr.onload = function() {
saveAs(xhr.response, 'immagine.jpg');
}
xhr.send(formdata)
the PHP
echo fread($img,filesize($img_path));

How to download the xls file using codeigniter

Here i have one button, if i click the button means i want to download the one demo.xls file,using php i did, but now i want to do codeigniter, i tried but i am not able to do? please see my below code
<button class="btn btn-warning" id="download-btn">
<i class="fa fa-download" aria-hidden="true"></i> Download Demo File
</button>
<script type="text/javascript">
$(document).ready(function(){
$("#download-btn").click(function(e){
e.preventDefault();
$.ajax({
type:'POST',
url :"Staff/downloadDemoFile",
cache: false,
contentType: false,
processData: false,
success: function(data) {
console.log(data);
},
error:function(exception){
alert('Exeption:'+exception);
}
});
});
});
</script>
My controller
public function downloadDemoFile()
{
if($this->session->logged_in != TRUE){
$this->load->view('login');
}
else{
$download= $this->Add_staff_model->downloadFile();
}
}
My model
public function downloadFile()
{
$sFileName = 'demo.xls';
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=$sFileName");
header("Content-Type: application/zip");
header("Content-Transfer-Encoding: binary");
// read the file from disk
readfile(UPLOAD_PATH_XLS.$sFileName);
}
Downloading a file from disk is pretty straightfoward using CodeIgniter Download Helper. Read the manual here.
public function downloadFile()
{
// load the helper somewhere, i.e in the constructor.
$this->load->helper('download');
// Use it when necessary
$sFileName = 'demo.xls'; // filename to be download. With path if necessary: /path/to/demo-xls
force_download($sFileName, NULL);
}
As a final consideration, in such a situation I wouldn't put the downloadFile() method in the model but in the controller itself.

Return images via PHP and set as src with AJAX

I have some problems when I try to display multiple images (well it doesn't work for one image so multiple is impossible), and what I'm doing is with my function of AJAX to recover from my db, all the images location string that are in the table images. Then it calls another function called setImages() which receives those strings of the image locations and I use iterate over the strings (using jQuery's .each()) to trigger an AJAX request that calls a php script named image.php?img=[data]. data is the string containing the image location, so I have something like the code below:
The problem is that setImages() of my js, doesn't show the images
the PHP file:
<?php
$init="/var/Imagenes/cursos/";
$img=$_GET['img'];
$path=$init.$img;
echo $path;
//el path debe ser autores/ or cursos/
$name="azure";
/*
header("Cache-Control: no-cache, must-revalidate");
header("X-Sendfile: $path");
header("Content-Type: image/jpeg");
header("Content-Disposition: inline; filename='archivos'");
*/
//el nombre de la base de datos de la imagen
header("Cache-Control: no-cache, must-revalidate");
if((isset($path)&& !is_null($path))){
header("X-Sendfile: $path");
if(strpos( $img, ".png" )||strpos( $img, ".PNG" )){
header("Content-Type: image/PNG;base64");
}
elseif(strpos( $img, ".jpg" )||strpos( $img, ".JPG" )){
header("Content-Type: image/jpg;base64");
}
elseif(strpos( $img, ".jpeg" )||strpos( $img, ".JPEG" )){
header("Content-Type: image/jpeg;base64");
}
else{
return "error.jpg";
}
$newimg=rand(1000 , 9999 );
header("Content-Disposition: inline; fileimg= $newimg-$img");
exit();
}
else{
echo "no se pudo realizar la consulta";}
JS code:
functions listImgCursos works fine...
function listImgCursos(identificador) {
var resultado= $.ajax({
url: consultaBasica,
cache: false,
type: 'POST',
data : { action: "imgCursos"}
}).then(
function(data){// Success
var flagErrFound = false;
var nf404 = "" ;
$.each(data,
function(index,datos){
if((datos['id']===null)||(datos['img']=="")||(datos['img']==null)){
nf404 = datos['id'];
flagErrFound= true;
}//if close
}//function close
)//each close
if(flagErrFound===true){
error = {error: "CX02", msj: "Failed to get data from records.", data: nf404 };
return $.Deferred().reject(error);
}
else
return data;
},//function sucessful
function(){// fail
error = {error: "CX01", msj: "Failed to execute ajax"};
return $.Deferred().reject(error);
}//function fail
);//then;
resultado.done(
function (data){//success
setImages(data);
}//function DONE
);
resultado.fail(
function(e){//function fail
console.log(e.msj + " "+ e.error + ":" + e.data );
}//function FAIL)
);
}
function setImages(data){
$.each(data, function (index, datos) {
var temp="../classes/imagen.php?img="+encodeURIComponent(datos['img'])+"&t="+((new Date).getTime());
console.log(temp); // returns something like: ../classes/imagen.php?img=curso-2561.jpg&t=1489074434134
$.ajax({
url: temp,
type: "GET",
dataType: "image/jpg;base64",
async:true,
cache: false,
success: function(datai){
console.log(datai);
$('#pickimg').append('<img src="data:image/png;base64,' + datai + '" />');
},
fail: function(){
}
});
});
The problem is that setImages() of my js, doesn't show the images
This is because of multiple reasons:
The PHP code isn't actually returning the file contents. To do that, use a function like file_get_contents(),
readfile(), etc. Also, the string should be base-64 encoded so use base64_encode().
$newimg=rand(1000 , 9999 );
header("Content-Disposition: inline; fileimg= $newimg-$img");
echo base64_encode(file_get_contents($path));
exit();
This may be redundant with the first bullet, but the Syntax for header Content-Disposition only contains three directives: name, filename and filename*.1. So that fileimg directive is invalid. That header could include the filename directive but because a string is being returned it would be useless:
header("Content-Disposition: inline; filename=\"$newimg-$img\"");
In that sense it isn't really returning an image, so the headers for Content-Type are basically incorrect. Consequently, the AJAX call (using $.ajax()) should not specify the dataType (i.e. dataType: "image/jpg;base64" which wouldn't be dynamic anyway - for jpgs, pngs, etc). So remove that dataType option.
See a demonstratation of this with the modifications applied in this phpFiddle. Click the button labeled Run - F9 - and then when the frame loads, click the button labeled Update to trigger an AJAX call to the PHP script which will load a PNG image and append it to the element with id attribute "pickimg".
1https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#Directives

AngularJS FileSaver producing empty file

I have the following PHP code which outputs a document from a web service:
$db->where('documentReference', $post->documentID);
$results = $db->getOne('documents');
$filelocation = 'doc/';
$file = $results['filename'];
header('Content-type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
header('Content-Length: ' . filesize($filelocation.$file));
header('Content-disposition: attachment; filename="'.$file.'"');
readfile($filelocation.$file);
And on the front end..
APIService.registerUser($scope.formData).then(function(data){
var blob = new Blob([data.data], {type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'});
var config = {
data: blob,
filename: 'test.docx'
};
FileSaver.saveAs(config);
});
}
When I inspect the data returned from the API the document get returned fine, but when being saved it's always empty?
When calling the API endpoint you need to set the responseType to array buffer like so:
var promise = $http.post('http://someurl', formData, {responseType: 'arraybuffer'});
return promise;
The file then gets saved properly.

d3.json() fail but jQuery $.getJSON() work correctly with same URL and php code

i work with d3.js and CodeIgniter and i have a problem when i get json data with d3.json
when i run this code :
var url = "http://probe.dev/draw/windRose?station=vp2&sensors=wind&Since=2012-10-15T00:00:00&StepUnit=DAY&StepNbr=6;
d3.json(url, function(d) {
console.log(d); // print NULL in console
}
but it works with
$.getJSON(url, function(d) {
console.log(d); // print my data object correctly
}
my php code is :
<?php
#ob_end_clean();
header('Content-Type: "application/json"');
header('Content-Disposition: attachment; filename="data.json"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
header("Content-Length: ".strlen($data));
exit($data);
I don't understand why d3.json do not work ?
I couldn't make it work with v2, I moved to d3js.v3 and it worked.
The callback function in v3 takes two params:
d3.json(url, function(error, json) {
if (error) return console.warn(error);
else ...;
}

Categories