PHP on the fly flush xml to zipfile and push download - php

I create a xml file based on information from my database (xmltv format). These xml files can be quite big - 25-70mb is normal. Now i create the xml file on the fly like this:
$xmlWriter = new XMLWriter();
$xmlWriter->openURI('php://output');
and flush through the loop to prevent memory overflow. I also set headers to push the content as download:
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $config->filename . '.xml"');
I would like to be able to zip/gzip the xml because of the size. Is this possible on the fly? I have used PHPZip before, which works good with files, but i dont know if i can write the xml output directly to the zip?

If I have understood correctly, the goal is to create gzip compressed data dynamically, without creating a file on the server. This is possible with deflate_init and deflate_add, but requires PHP 7.
$gzip = deflate_init(ZLIB_ENCODING_GZIP, array('level' => 9));
$data = deflate_add($gzip, "my", ZLIB_NO_FLUSH);
$data .= deflate_add($gzip, "data", ZLIB_FINISH);
With deflate_add we can add more data any number of times (the mode should be ZLIB_FINISH for the last chunk).
We can adjust this method using XMLWriter::openMemory (stores the data in memory) and XMLWriter::flush, to zip the xml elements as they are produced, and create the contents of a .gz file on the fly. For example this code:
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="file.gz"');
$xmlWriter = new XMLWriter();
$xmlWriter->openMemory();
$xmlWriter->startDocument('1.0', 'UTF-8');
$gzip = deflate_init(ZLIB_ENCODING_GZIP, array('level' => 9));
for ($i = 0; $i < 10; $i++) {
$xmlWriter->writeElement("element", $i);
$data = $xmlWriter->outputMemory();
echo deflate_add($gzip, $data, ZLIB_NO_FLUSH);
}
echo deflate_add($gzip, "", ZLIB_FINISH);
creates xml elements, compresses and outputs them one by one, without using too much memory or any files.

Related

how to create another sheet while write data using fputcsv in php

I am using fputcsv for write data. It is working fine. what i need is i need to create another sheet like any name and i need to write data on that. How to do that? Please guide. I am not familiar with this.
This is my code.
$filename = date("Y-m-d").".csv";
header('Content-type: application/csv');
header('Content-Disposition: attachment; filename=' . $filename);
header("Content-Transfer-Encoding: UTF-8");
$fp = fopen('php://output', 'a');
$yetToaproveFields = array('job_no'=>'Job Number',
'revisioncycle'=> 'Revision Cycle',
'version'=>'Version',
'is_approved'=>'Approved Status'
);
fputcsv($fp, $yetToaproveFields);
$notApproveArr = array();
$notApprovedValues = self::getNotApprovedJobs($_REQUEST);
foreach ($notApprovedValues as $key=>$value) {
if ($value['is_approved'] == 1)
$value['is_approved'] = 'No';
fputcsv($fp, $value);
}
fclose($fp);
CSV doesn't support multiple sheets, it's a simple text file. To use sheets, you should use an Excel extension, such as PHPExcel.
You can't.
CSV is a very simple data file format. It doesn't support spreadsheet features like styling or multiple worksheets. If you want multiple worksheets, then you need to use a spreadsheet file format like BIFF (.xls), OfficeOpenXML (.xlsx) or OASIS (.ods)

Mozilla Firefox not correctly downloading certain file types from MySQL database

I have a MySQL database where I store various file types. If the file extension is the standard three character (.doc, .xls, .pdf), then the content type is stored as application/msword, application/ms-excel, application/pdf, etc. If it's .docx or .xlsx, then the content type is application/vnd.openxmlformats-officedocument.
Until recently, this has never been a problem, but within the past few weeks, it's become a problem in Firefox. Firefox will not download files of type application/vnd.openxlmformats-officedocument in their correct formats. Instead, it downloads the file without an extension and the user has to add it manually. Furthermore, if there are spaces in the filename, then Firefox only picks up the first word in it and that's how the file is saved.
Here is the code I use to upload files:
if($_FILES['Budget']['size'] > 0)
{
$fileName = $_FILES['Budget']['name'];
$tmpName = $_FILES['Budget']['tmp_name'];
$fileSize = $_FILES['Budget']['size'];
$fileType = $_FILES['Budget']['type'];
$fp = fopen($tmpName, 'r');
$content = fread($fp, filesize($tmpName));
fclose($fp);
$fileUp = $con->prepare("INSERT INTO ptfs.upload (ProposalNo, name, size, type, content) VALUES(:proposalno,:name,:size,:type,:content)");
$fileData=array('proposalno'=>$proposalNo,'name'=>$fileName,'size'=>$fileSize,'type'=>$fileType,'content'=>$content);
$fileUp->execute($fileData);
}
And here is the code for presenting the file link to the user:
if(isset($_GET['ProposalNo']) && isset($_GET['UID']))
{
$fileget = $con->prepare("SELECT name, type, size, content FROM upload WHERE ProposalNo = :proposalno AND UID = :uid");
$data = array('proposalno'=>$_GET['ProposalNo'],'uid'=>$_GET['UID']);
$fileget->execute($data);
list($name, $type, $size, $content) = $fileget->fetch(PDO::FETCH_BOTH);
header("Content-Disposition: attachment; filename=$name");
header("Content-type: $type");
header("Content-length: $size");
echo $content;
exit;
}
This works fine in every browser except Firefox, and as I said, it's a recent problem. My users started reporting it within the last couple of weeks. Can I modify either my code or my database to make sure that FF downloads these file types correctly again?
"Furthermore, if there are spaces in the filename, then Firefox only picks up the first word in it and that's how the file is saved."
It's always best to catch the problem right away (before the file is uploaded and entered into DB) and replace spaces with underscores, then let PHP do its thing afterwards.
Consider the following which is the logic I use for my uploaded files, which will transform:
This is a line
into:
This_is_a_line
<?php
$string = "This is a line";
$arr = explode(" ",$string);
$string = implode("_",$arr);
echo $string;
?>
This taken from my own experiences with the same issue that resolved it.

Giving images random name in PHP

this way i load image using php:
header("Content-type: image/jpeg");
$image=imagecreatefromjpeg("http://i.imgur.com/zWaQJNCb.jpg");
imagejpeg($image);
but problem is, if i want to save the image manually from my web browser to my desktop then all the images has same name like ix.jpeg [here file name is: ix.php] but i cant understand what is the way to configure the header so that images will have random name.. like 25xc.jpeg, 36s5a2f.jpeg... while saving it on desktop.. any idea?
this is will do the job!
$dt = date(time());
header("Content-type: image/jpeg");
header('Content-Disposition: inline; filename="'. $dt .'.jpg"');
$image=imagecreatefromjpeg("http://i.imgur.com/knNxDFnb.jpg");
imagejpeg($image);
Use the filename value in your header call. See Example 1.
<?php
// We'll be outputting a PDF
header('Content-type: application/pdf');
// It will be called downloaded.pdf
header('Content-Disposition: attachment; filename="downloaded.pdf"');
// The PDF source is in original.pdf
readfile('original.pdf');
?>
I'll leave the "generate a random string" part up to you. I'd suggest basing it on the checksum of the file, but that's just me.
This is what you need to do if you would like to generate unique names for your users, all you need to do is to use actual time stamp and use the filename parameter in the header, you can do like below (if you use random generation there will be a very few cases where you will get the same name twice) :
$dt = date(time());
header("Content-type: image/jpeg");
header('Content-Disposition: attachment; filename="'. $dt .'.jpg"');
The above will generate unique images names like below :
1362504465.jpg
I hope this helps.
There are dozens if not hundred ways to do that.
Append time() after file name. This way all the file names will
have a (unique)number.
Use a hashing function, like md5().
Use this code-
function generateRandomName(len) {
$out = '';
for($i=0; $i<len; $i++) {
$out.=chr(rand(65,122));
}
return $out;
}
Simply append rand function after your file name (But then the file names won't be of equal length).
Google is your friend.
This would make an 8 characters long name of letters a-z, but you could do some magic and/or use http://www.asciitable.com/
function randChar() {
return char(rand(97, 122);
// Then you could either do a random number from 97 to 122 for a-z or just make an array of all the characters you would like in the filename.
}
$filename = '';
$amountOfChars = 8;
for($i = 0; $i < $amountOfChars; $i++; ) $filename .= randChar();
Hope this helps

place file into zip and stream to browser - zero physical files

basically we currently generate a csv file and push it too the clients browser to download, so no physical files are involved or stored as these are reports that always change.
These have become quite large in some places and I need to reduce the speed and size of the downloads to clients.
I decided to go the zip root and got the below test script working fine, however i am still having to generate the zip file physically and then later delete it, obviously there is a speed factor here as I am writing and reading the file, when I only need to push the zip files contents to the browser.
is there no way to create a stream with the ziplib?
<?php
//report
$reportFilename = 'report';
$data = '005,"756607 ","WED","L","TEST UITENHAGE CBD F NFD"
005,"756608 ","MON","L","TEST SUMMERSTRAND NNB "
005,"756634 ","WED","L","TEST UITENHAGE PENFORD F"
005,"756776 ","MON","L","TEST WALKER DRIVE FOOD "
005,"756858 ","MON","C","TEST R ADAMI&SONS C C "
005,"801002 ","MON","L","TESTMOFFET NNB "
005,"CP00270 ","WED","L","TEST WALMER P FNF "
'; //dummy data
// populate fake data...
$reportData = $data.$data.$data.$data.$data.$data.$data.$data.$data.$data.$data;
//make unique so no clashing occurs
$zipFilename = '_temp_'.microtime().$zipFilename;
//zip data
$zip = new ZipArchive();
$zip->open($zipFilename, ZIPARCHIVE::CREATE);
$zip->addFromString($reportFilename.'.csv' , $reportData); //add report
$zip->close();
$zipData = file_get_contents($zipFilename);
$zipSize = filesize($zipFilename);
unlink($zipFilename);
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=\"".$reportFilename.".zip\"");
header('Content-Transfer-Encoding: binary');
header("Content-Type: application/zip");
echo $zipData;
?>

Export CSV from Mysql

I'm having a bit of trouble exporting a csv file that is created from one of my mysql tables using php.
The code I'm using prints the correct data, but I can't see how to download this data in a csv file, providing a download link to the created file. I thought the browser was supposed to automatically provide the file for download, but it doesn't. (Could it be because the below code is called using ajax?)
Any help greatly appreciated - code below, S.
include('../cofig/config.php'); //db connection settings
$query = "SELECT * FROM isregistered";
$export = mysql_query($query) or die("Sql error : " . mysql_error());
$fields = mysql_num_fields($export);
for ($i = 0; $i < $fields; $i++) {
$header .= mysql_field_name($export, $i) . "\t";
}
while ($row = mysql_fetch_row($export)) {
$line = '';
foreach ($row as $value) {
if ((!isset($value) ) || ( $value == "" )) {
$value = "\t";
} else {
$value = str_replace('"', '""', $value);
$value = '"' . $value . '"' . "\t";
}
$line .= $value;
}
$data .= trim($line) . "\n";
}
$data = str_replace("\r", "", $data);
if ($data == "") {
$data = "\n(0) Records Found!\n";
}
//header("Content-type: application/octet-stream"); //have tried all of these at sometime
//header("Content-type: text/x-csv");
header("Content-type: text/csv");
//header("Content-type: application/csv");
header("Content-Disposition: attachment; filename=export.csv");
//header("Content-Disposition: attachment; filename=export.xls");
header("Pragma: no-cache");
header("Expires: 0");
echo 'Download Exported Data'; //want my link to go in here...
print "$header\n$data";
In essence, you can't output the CSV file and the link to it in one go. (You need to introduce the concept of a page "mode" and activate the download mode via a ...pagename.php?mode=download or similar. You could then use PHP's switch statement to switch on $_GET['mode'] in your script.)
That said, the text/csv content type header you were using is correct, although you may also want to output the Content-Length and Content-Disposition headers. After you've output the file data, also be sure to stop any additional script processing via PHP's exit function.
Additionally, it would probably be a lot less hassle (and will certainly be faster/more memory efficient) to use MySQL SELECT ... INTO OUTFILE facility (if you have the permissions) rather than use PHP to gather the data.
You can't have text and a download on the same page. You need to have a link to the download area, which could just be a GET parameter leading to a function, which then does all the processing, displays headers, and echoes the content of the CSV.
For example, you could have Click here to download CSV, then in your code have if ($_GET['action'] === 'download'), get the data from the database, format it, send the headers, and echo the data. And then die(), because that part of the script can accomplish no more.
You should not put the link in the same file that generates the csv, as the link will not be in the csv itself!
Do something like:
Download CSV
and it should work
Three things to consider:
You're sending headers indicating that the user is going to be downloading a CSV file, but then you send create a link to download it? This isn't correct, you should be linking to this page, and then only outputting the CSV data itself after the headers.
MySQL has the ability to generate CSV output, and you should definitely take advantage of this instead of trying to do it yourself. You can use SELECT INTO ... OUTFILE to do this.
If you must create the CSV using PHP, please use fputcsv to do so. This will handle all the complications of CSV such as escaping and proper formatting. Since fputcsv writes to a file, you could either write it to a temporary file and then output it after you send your headers, or use the following trick to output it directly:
Do this after sending headers:
$fp = fopen('php://output', 'w');
while( $row = mysql_fetch_row( $export ) ) {
fputcsv($fp, $row);
}
I think the mySQL => CSV is common problem which is part of each PHP forum.
I have try to solve this issue in a common way and implement an free export
lib for PHP which is very similar to the Google AppInventor philosophie.
DragDrop and hide the coding stuff.
Use the lib and create your Export via Click&Point.
Common Demos: http://www.freegroup.de/software/phpBlocks/demo.html
Link to editor: http://www.freegroup.de/test/editor/editor.php?xml=demo_sql.xml
worth a look
Greetings
Andreas

Categories