get empty file after download - php

i want to download a file of projet but i get it empty. i'am using a spreadsheet librairy
Notice : i a make a dump after save function , my file is full and not empty in the path directory of project
Someone can help me !
bellow is my code :
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load('template.xlsx');
$worksheet = $spreadsheet->getActiveSheet();
$filename = 'write.xls';
$worksheet->getCell('A1')->setValue('John');
$worksheet->getCell('A2')->setValue('Smith');
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xls');
$writer->save($filename); die;
// to download file
header('Content-Type: application/vnd.ms-excel');
header("Content-Length:".filesize($filename));
header("Content-Disposition: attachment;filename=$filename");
header('Cache-Control: max-age=0');
$writer->save('php://output');
exit();
i except a full file after downloading it

This function would work:
define ("ONE_DAY", 86400);
function getExisting()
{
$rootFolder = "pathTodirectory";
//first clear old files
$files = scandir($rootFolder,1);
array_pop($files); array_pop($files);
foreach($files as $file)
{
$fp = $rootFolder . DIRECTORY_SEPARATOR . $file;
$filemtime=filemtime($fp);
if (time() - $filemtime >= (2 * ONE_DAY))unlink($fp);
}//end clearing old files
//second rescan folder for current files
$files = scandir($rootFolder,1);
array_pop($files); array_pop($files);
$existing = array_reverse($files);
return $existing;
}
$existing = getExisting();
echo "\n<p> Select file or enter office number to review inventory:";
echo "\n <ul>";
foreach($existing as $rpt)
{
$spd = "pathTodirectory" . $rpt; \\make sure to follow up with relative path name here also
echo "\n <li><a href=\"$spd\" >" . $rpt ."</a></li>";
}
echo "\n </ul>";

I think it is the load() usage issue, your code works with following correction in my site :
$file_loc = 'template.xlsx';
$file_type = \PhpOffice\PhpSpreadsheet\IOFactory::identify($file_loc);
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($file_type);
// $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load('template.xlsx');
$spreadsheet = $reader->load($file_loc);
$worksheet = $spreadsheet->getActiveSheet();
$filename = 'write.xls';
$worksheet->getCell('A1')->setValue('John');
$worksheet->getCell('A2')->setValue('Smith');
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xls');
// save a physical file in server, you can skip this actually
$writer->save($target_dir . $filename);
// die; // don't die, be happy (^_^)
// to download file
header('Content-Type: application/vnd.ms-excel');
header("Content-Length:" . filesize($filename));
header("Content-Disposition: attachment;filename=$filename");
header('Cache-Control: max-age=0');
$writer->save('php://output');
exit();

Related

PhpSpreadsheet - Download file instead of saving it

I need to generate an excel file (xls) and trigger the download after it is generated.
I found this example in the documentation.
<?php
require 'vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValue('A1', 'Hello World !');
$writer = new Xlsx($spreadsheet);
$writer->save('hello world.xlsx');
It shows how to create a excel file and save it on the server.
How can I serve the result to the client instead and "force" him to download it?
I need to get the data of the $writer somehow.
I am currently solving it without PhpSpreadsheet:
// Excel Export
$filename = 'export_'.date('d-m-y').'.xls';
$filename = $validator->removeWhitespace($filename);
header('Content-type: application/ms-excel');
header('Content-Disposition: attachment; filename='.$filename);
exit($response["output"]); // <-- contains excel file content
But it is not working with my delimiter (semicolon). The semicolon is not getting interpreted and everything is getting written into one column.
If I export it as .csv, then it works. But I need it as .xls or .xlsx
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
class DownloadExcel
{
public static function createExcel(array $data, array $headers = [],
$fileName = 'data.xlsx')
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
for ($i = 0, $l = sizeof($headers); $i < $l; $i++) {
$sheet->setCellValueByColumnAndRow($i + 1, 1, $headers[$i]);
}
for ($i = 0, $l = sizeof($data); $i < $l; $i++) { // row $i
$j = 0;
foreach ($data[$i] as $k => $v) { // column $j
$sheet->setCellValueByColumnAndRow($j + 1, ($i + 1 + 1), $v);
$j++;
}
}
$writer = new Xlsx($spreadsheet);
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment; filename="'. urlencode($fileName).'"');
$writer->save('php://output');
}
}
This is what I use to create a spreadsheet with PhpSpreadsheet and output directly to php://output for download.
I had the same problem and found a solution here : https://github.com/PHPOffice/PhpSpreadsheet/issues/217
I ended my method with $writer->save('php://output'); then exit()
My answer :
PHP:
$writer = new Xlsx($spreadsheet);
ob_start();
$writer->save('php://output');
$ret['data'] = base64_encode(ob_get_contents());
ob_end_clean();
JS:
var linkSource = 'data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,'+ response.data ;
var downloadLink = document.createElement("a");
var fileName = 'clients.' + format;
downloadLink.href = linkSource;
downloadLink.download = fileName;
downloadLink.click();
I solved it with a workaround. I temporarily save the file on the server, then I load the content into a variable and serve it as a download file. Then I delete the file from the server.
Workaround:
$date = date('d-m-y-'.substr((string)microtime(), 1, 8));
$date = str_replace(".", "", $date);
$filename = "export_".$date.".xlsx";
try {
$writer = new Xlsx($response["spreadsheet"]);
$writer->save($filename);
$content = file_get_contents($filename);
} catch(Exception $e) {
exit($e->getMessage());
}
header("Content-Disposition: attachment; filename=".$filename);
unlink($filename);
exit($content);
call ob_end_clean(); just before the $writer->save('php://output').
ob_end_clean();
$writer->save('php://output');
This worked for me:
$excel = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $excel->getActiveSheet();
$sheet->setTitle('This is a test', true);
ob_end_clean();
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="filename_' . time() . '.xlsx"');
header('Cache-Control: max-age=0');
$xlsxWriter = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($excel, 'Xlsx');
$xlsxWriter = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($excel);
exit($xlsxWriter->save('php://output'));
If you have problems where the files download corrupted, it is always good to check if there is any extra whitespace at the top of your file output. If your PHP files have blank white lines, whilst HTML won't have a problem, your phpspreadsheet file will. Spent a good chunk of time trying to fix these issues but the problem was with the whitespace!

How to excel password protected in zip file in php

I want to download excel in zip folder. After that, it should be password protected. But it does not work.
public function download (){
$header = array('id');
require_once APPPATH."/third_party/PHPExcel.php";
$sheet = new PHPExcel();
$file = $this->appmodel->Data();
// echo "<pre>"; print_r($file); die;
$filename = $file->id;
$this->load->helper('date');
$date = date('Y-m-d');
//1st Sheet
$sheet->setActiveSheetIndex(0);
$activeSheet = $sheet->getActiveSheet();
$activeSheet->fromArray($header, null);
$objWriter = PHPExcel_IOFactory::createWriter($sheet, 'Excel2007');
// echo "<pre>"; print_r($objWriter); die;
$excel_file_tmp = tempnam("/tmp", 'your_prefix');
$objWriter->save($excel_file_tmp);
//zip
$zip_file_tmp = tempnam("/tmp", 'your_prefix');
$zip = new ZipArchive();
$zip->open($zip_file_tmp, ZipArchive::OVERWRITE);
$zip->addFile($excel_file_tmp, 'your_name.xlsx');
$zip->close();
//download
$password = "22";
$download_filename = 'your_name.zip';
header("Content-Type: application/octet-stream");
header("Content-Length: " . filesize($zip_file_tmp));
header("Content-Disposition: attachment; filename=\"" . $download_filename . "\"");
#system("zip -P $password $excel_file_tmp $zip_file_tmp ");
readfile($zip_file_tmp);
// unlink($excel_file_tmp);
// unlink($zip_file_tmp);
#unlink($zip_file_tmp);
}
Since PHP >7.2 you can use setEncryptionName to procted a ZIP archive with a password.
if ($res === TRUE) {
$zip->addFromString('FILENAME_WITH_EXTENSION', 'file content goes here'); //Add your file name
$zip->setEncryptionName('FILENAME_WITH_EXTENSION', ZipArchive::EM_AES_256, 'PASSWORD'); //Add file name and password dynamically
$zip->close();
echo 'ok';
} else {
echo 'failed';
}
See more at Protect ZIP Archive with password

Add both folders and files into zip (PHP)

I am looking to add both folders and files from a matching array to a zip file. Below is my code:
function listdir($start_dir='./assets') {
//Array that will contain the director
$files = array();
if (is_dir($start_dir)) {
$fh = opendir($start_dir);
while (($file = readdir($fh)) !== false) {
// Loop through the files, skipping '.' and '..', and recursing if necessary
if (strcmp($file, '.')==0 || strcmp($file, '..')==0) continue;
$filepath = $start_dir . '/' . $file;
if ( is_dir($filepath) )
$files = array_merge($files, listdir($filepath));
//else
array_push($files, $filepath);
}
closedir($fh);
} else {
// false if the function was called with an invalid non-directory argument
$files = false;
}
return $files;
}
//Array of all files
$allFiles = listdir('./assets');
print_r($allFiles);
//Gets all values with name "asset"
$name = $_POST['asset'];
//If there are items in the array, zip them together
if($name!=0){
//Compares $_POST array with array of all files in directory
$result = array_intersect($allFiles, $name);
function zipFilesAndDownload($result){
$zip = new ZipArchive();
//create the file and throw the error if unsuccessful
if ($zip->open('SelectedAssets.zip', ZIPARCHIVE::CREATE )!==TRUE) {
exit("cannot open SelectedAssets.zip\n");
}
//add each files of $file_name array to archive
foreach($result as $allFiles) {
$zip->addFile($allFiles);
}
$zip->close();
$zipped_size = filesize('SelectedAssets.zip');
header("Content-Description: File Transfer");
header("Content-type: application/zip");
header("Content-Type: application/force-download");// some browsers need this
header("Content-Disposition: attachment; filename=SelectedAssets.zip");
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header("Content-Length:". " $zipped_size");
ob_clean();
flush();
readfile("SelectedAssets.zip");
unlink("SelectedAssets.zip"); // Now delete the temp file (some servers need this option)
exit;
}
if(isset($_POST['submit'])) {
//$file_names=$_POST['assets'];// Always sanitize your submitted data!!!!!!
//$file_names = filter_var_array($_POST['assets']);//works but it's the wrong method
$filter = filter_input_array(INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS) ;
$file_names = $filter['assets'] ;
//Archive name
$archive_file_name='DEMO-archive.zip';
//Download Files path
$file_path= getcwd(). './';
//call the function
zipFilesAndDownload($result);
} else {
print 'Something went wrong.';
exit;
}
} //Otherwise, don't.
else {
print_r("Please select a file.");
}
The code above searches through a directory and reads all the folders and files. Then using array_intersect I matched files that were put into an empty array by the user to all the files in the previously searched directory. I then zip the files that match and download.
My question is, how can I have a folder be added to this zip as well? As of now, this only adds files and folders are assumed as empty.

How to keep line breaks in a txt readme file when zipped by addfile in php?

I have a txt file showing copyright info and address to be zipped together with my images. I use php to do that. All works fine but when opening the ReadMe.txt after downloading the zip, all linebreaks are gone. How to keep the line breaks? The code I use:
$date = date("y-m-d");
$zip = new ZipArchive;
$download = 'pics-' . $date . '.zip';
$zip->open($download, ZipArchive::CREATE);
foreach (glob("photos/*.jpg") as $file) { /* Add appropriate path to read content of zip */
$new_filename = substr($file,strrpos($file,'/') + 1);
$zip->addFile($file,$new_filename);
//$zip->addFile($file);
}
$zip->addFile('/info/readme.txt', 'ReadMe.txt');
$zip->close();
header('Content-Type: application/zip');
header("Content-Disposition: attachment; filename = $download");
header('Content-Length: ' . filesize($download));
header("Location: $download");
Try out the following
foreach (glob("photos/*.jpg") as $file) { /* Add appropriate path to read content of zip */
$new_filename = substr($file,strrpos($file,'/') + 1);
$file=$new_filename."<br />"
$zip->addFile($file);
}

PHP output empty ZIP

This script successfully generated the pdfs to a folder tmp/....
However the ZIP output to the browser is empty and I don't know what I have done wrong.
$file = tempnam("tmp", "zip");
$zip = new ZipArchive();
// Zip will open and overwrite the file, rather than try to read it.
$zip->open($file, ZipArchive::OVERWRITE);
foreach( explode( ',', $_POST["ids"]) as $Client_ID)
{
$sql_qry="select *
from ca_client_statement
where client_id='".$Client_ID."' and trading_period_month like '".$TP_Month."'";
$sql_err_no=sql_select($sql_qry,$sql_res,$sql_row_count,$sql_err,$sql_uerr);
//echo $sql_qry;
//echo '<br/>';
$row = mysql_fetch_assoc($sql_res);
$file_content = $row['pdf_statement'];
$file_name = 'tmp/'.$Client_ID.'statement.pdf';
$pdf=file_put_contents($file_name, $file_content);
$zip->addFile($pdf);
}
$zip->close();
// Stream the file to the client
header("Content-Type: application/zip");
header("Content-Length: " . filesize($file));
header("Content-Disposition: attachment; filename=\"a_zip_file.zip\"");
readfile($file);
unlink($file);
file_put_contents() returns the number of bytes written, not the file name. Try changing the line right after it to this:
$zip->addFile($file_name);

Categories