I have a PHP application that downloads MySQL table data to a spreadsheet on the client machine. I found some code in numerous places on the web that works fine for Open Office Scalc on a machine running Redhat. The problem I ran into was when I tried to download to an MS Excel on a Windows PC rather than to an Open Office spreadsheet. The problem seems to be associated with the length of the string inserted into a cell. If it is too long, Excel thinks the file is corrupt and won't load it. "Too long" seems to be about 255 characters, even though the MS Excel specs say 32,000 is the maximum length.
To investigate the problem further, I tried downloading the same data as a tab-separated values file and let Excel convert it to a spreadsheet. Using that method, there was no problem loading very long strings into the spreadsheet cells. So strings much longer than 255 characters can in fact be inserted into Excel cells, but just not with the code I am using, even though that code works with Open Office. Using tsv files would not solve our problem, because the long strings have carriage returns that we want to retain, and carriage returns are interpreted as row separators when a tsv file is loaded into a spreadsheet.
The PHP function that writes a string to a spreadsheet cell is:
function xlsWriteLabel($Row, $Col, $Value ) {
$L = strlen($Value);
echo pack("ssssss", 0x204, 8 + $L, $Row, $Col, 0x0, $L);
echo $Value;
return;
}
The other necessary code for transfers to spreadsheets in addition to the above function can be found at:
http://www.appservnetwork.com/modules.php?name=News&file=article&sid=8
I haven't found any explanations as to the meanings of the various arguments passed to the "pack" function in the above code, and I'm wondering if changing one the arguments in the function above could solve the problem.
So, if anyone has a solution to this problem, I'd be interested in hearing it.
...unless your text has double quotes.
What you should use is proper delimiters from the ASCII char set.
Seq Dec Hex Acro Name
^\ 28 1C FS File Separator
^] 29 1D GS Group separator
^^ 30 1E RS Record Separator
^_ 31 1F US Unit separator
I don't know php but in perl a print statement might look like:
print chr(30), "Field1", chr(30), "Field2", chr(30), "Field3", chr(31);
I have written a simple approach to generate absolute excel file.
A simple function.
First you convert your contents to an Array (it is very simple and everybody know.). Then pass the array to my function given below.
<?php
//array to excel
function arrayToExcel($array, $filename='List')
{
if (!is_array($array) or !is_array($array[0])) return false;
$xl = pack("ssssss", 0x809, 0x8, 0x0, 0x10, 0x0, 0x0); //begining of excel file
$i = 0;
foreach ($array as $key => $val)
{
$j=0;
foreach ($val as $cell_key => $cell_val)
{
$cell_val = trim($cell_val);
$length = strlen($cell_val);
//checking cell value is a number and the length not equal to zero
if(preg_match('/^[0-9]*(\.[0-9]+)?$/', $cell_val) && $length!=0) {
$xl .= pack("sssss", 0x203, 14, $i, $j, 0x0); //writing number column
$xl .= pack("d", $cell_val);
}
else {
$xl .= pack("ssssss", 0x204, 8 + $length, $i, $j, 0x0, $length); //writing string column
$xl .= $cell_val;
}
$j++;
} //end of 2nd foreach
$i++;
} //end of first foreach
$xl .= pack("ss", 0x0A, 0x00); //end of excel file
$filename = $filename.".xls";
header("Pragma: public");
header('Content-Type: application/vnd.ms-excel');
header("Content-Disposition: attachment; filename=$filename");
header("Cache-Control: no-cache");
echo $xl;
} //end of arrayToExcel function
//eg: arrayToExcel($myarray, "contact_list");
?>
Surround your cell values with double quotes "", it will allow you to import the data in a tsv or csv format without having to worry about the carriage returns being interpreted as a new row. You could also use the fputcsv function from PHP to export a tsv or csv file that excel can read.
This is the slightly more efficient version of their functions thank you linked too, but I don't know if the format they output is actually correct for excel or not.
function xlsBOF(){
echo pack("s6", 0x809, 0x8, 0x0, 0x10, 0x0, 0x0);
}
function xlsEOF(){
echo pack("ss", 0x0A, 0x00);
}
function xlsWriteNumber($Row, $Col, $Value){
echo pack("s5d", 0x203, 14, $Row, $Col, 0x0, $Value);
}
function xlsWriteLabel($Row, $Col, $Value){
$L = strlen($Value);
echo pack("s6A*", 0x204, 8 + $L, $Row, $Col, 0x0, $L, $Value);
}
Related
Iam creating a simple PHP application, where is a csv file upload then an .stm(its a simple text file) file download. The records must be write at given byte position. For example: 1st record position 1, length 2, 2nd record, etc.Records
So Im trying to use fwrite, fseek to get to given position, for example I dont need 2 record to be filled, so I jump to position 11 but there are NUL characters in the file.
Code:
$input = $sFileName;
//a nevbol kiszedni a datumot, majd EEEEHHNN verziora konvertalni
$get_date_from_name = str_replace(".","",substr($sFileName,0,10));
$output = $get_date_from_name . '_mpl_tensoft_import.stm';
$_SESSION['file_name'] = $output;
if (false !== ($ih = fopen($input, 'r'))) {
$oh = fopen($output, 'w+b');
//Create first line of the file 11 record type
fwrite($oh,"11");
fseek($oh, 11);
fwrite($oh,"120967120159857100100007");
/*while (false !== ($data = fgetcsv($ih,0,";"))) {
// this is where you build your new row
//Interne cislo, Dodavatel, Dodavacie c.faktury Celk.suma, Dátum prijatia, Dátum splat. = Datum uhrady, Predmet
$outputData = array($data[0], $data[1], $data[2], $data[3], $data[12], $data[13], $data[36] );
fputcsv($oh, $outputData);
}*/
fclose($ih);
fclose($oh);
unlink($input);
}
Result is:
11NULNULNULNULNULNULNULNULNUL120967120159857100100007
Visual Studio Code gives an error that the file is binary or to large or uses an unsupported encoding.
Notepad++ adds the NUL characters.
Should I just use a whitespace character? 1 whitespace character equals 1 byte? The file should be in CP852 encoding
Thanks for the help and pointing in to the right direction.
Your seek() offset seems to be "out of range".
Documentation said :
In general, it is allowed to seek past the end-of-file; if data is then written, reads in any unwritten region between the end-of-file and the sought position will yield bytes with value 0.
EDIT :
If you want to put the second number at offset 13, using spaces, you could do something like this :
$ban_pos = 13 ;
$num = "11" ;
$pos = $ban_pos - strlen($num) ;
$ban = "120967120159857100100007" ;
$oh = fopen($output, 'w+');
fwrite($oh, $num);
fwrite($oh, str_repeat(' ', $pos));
fwrite($oh, $ban);
fclose($oh);
The file will contains :
11 120967120159857100100007
I am trying to export the data from PHP & MySql. I am getting Empty value for Fields which are having huge data(around 1000 chars). Except those fields everything is working fine.
This is the code which i am using right now. Please check once and let me know if any modifications. I searched in google, so many said its cache/memory problem.
<?php
function xlsBOF() {
echo pack("ssssss", 0x809, 0x8, 0x0, 0x10, 0x0, 0x0);
return;
}
function xlsEOF() {
echo pack("ss", 0x0A, 0x00);
return;
}
function xlsWriteNumber($Row, $Col, $Value) {
echo pack("sssss", 0x203, 14, $Row, $Col, 0x0);
echo pack("d", $Value);
return;
}
function xlsWriteLabel($Row, $Col, $Value ) {
$L = strlen($Value);
echo pack("ssssss", 0x204, 8 + $L, $Row, $Col, 0x0, $L);
echo $Value;
return;
}
$sel_sql=mysql_query("select desc from table");
$myFile = date("m-d-Y").'_users.xls';
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-type: application/vnd.ms-excel");
header("Content-Type: application/download");;
header("Content-Disposition: attachment;filename=".$myFile);
header("Content-Transfer-Encoding: binary ");
// XLS Data Cell
xlsBOF();
xlsWriteLabel(0,1,"desc");
$xlsRow = 1;
while(list($desc)=mysql_fetch_row($sel_sql)) {
xlsWriteLabel($xlsRow,1,"$desc");
$xlsRow++;
}
xlsEOF();
?>
Assuming you're using an xls file output: in a BIFF format file, a cell is limited to 32,767 characters; but only 1,024 characters are displayed in the cell. All 32,767 characters are display in the formula bar.
EDIT
Reading the code that you've actually posted:
Excel has limits on the amount of data a cell can hold: for Excel BIFF 8 files, that limit is 32,767 characters. However, for long strings, this data is maintained in the BIFF file across several blocks with continuation records, For BIFF 5 files (Excel 95) the limit is 2084 bytes per block; in BIFF 8 files (Excel 97 and above) the limit is 8228 bytes. Records that are longer than these limits must be split up into CONTINUE blocks.
This relatively simplistic writer isn't written to handle splitting the record into multiple continuation records: it doesn't even use the BIFF 8 shared string table, or indicate what BIFF version it is writing (which means Excel will open it using lowest common denominator parameters). It simply tries to store the entire contents of the cell into a standard label block. To fix this, you'd need to fix your code to handle splitting the string values with continuation blocks (via a shared string table); or switch to using a library that does handle splitting the shared strings across multiple blocks already.
I am using this simple function (taken from here) to export PHP array into simple binary Excel file. Writing binary Excel file was my requirement.
public static function array_to_excel($input)
{
$ret = pack('ssssss', 0x809, 0x8, 0x0, 0x10, 0x0, 0x0);
foreach (array_values($input) as $lineNumber => $row)
{
foreach (array_values($row) as $colNumber => $data)
{
if (is_numeric($data))
{
$ret .= pack('sssssd', 0x203, 14, $lineNumber, $colNumber, 0x0, $data);
}
else
{
$len = strlen($data);
$ret .= pack('ssssss', 0x204, 8 + $len, $lineNumber, $colNumber, 0x0, $len) . $data;
}
}
}
$ret .= pack('ss', 0x0A, 0x00);
return $ret;
}
Then to call this is pretty much simple simple:
Model_Utilities::array_to_excel($my_2d_array);
Function itself works great and is super simple to create simple binary PHP file. The problem I have is with UTF-8 characters. I get strange characters like Ä¡ instead of right characters... Is there a way to set character encoding in my to excel function?
EDIT:
After wading through hundreds of obfuscated Microsoft docs before locating the OpenOffice version of the XLS format spec, I managed to do something.
However, it relies on the BIFF8 format since, as far as I can tell, BIFF5 (the format used by Excel95) has no direct UTF-16 support.
function array_to_excel($input)
{
$cells = '';
foreach (array_values($input) as $lineNumber => $row)
{
foreach (array_values($row) as $colNumber => $data)
{
if (is_numeric($data))
{
$cells .= pack('sssssd', 0x203, 14, $lineNumber, $colNumber, 0x0, $data);
}
else
{
$data = mb_convert_encoding ($data, "UTF-16LE", "UTF-8");
$len = mb_strlen($data, "UTF-16LE");
$cells .= pack('ssssssC', 0x204, 9+2*$len, $lineNumber, $colNumber, 0x0, $len, 0x1).$data;
}
}
}
return pack('s4', 0x809, 0x0004, 0x0600, // <- this selects BIFF8 format
0x10) . $cells . pack('ss', 0x0A, 0x00);
}
$table = Array (
Array ("Добрый день", "Bonne journée"),
Array ("tschüß", "こんにちは。"),
Array (30, 40));
$xls = array_to_excel($table);
file_put_contents ("sample.xls", $xls);
My (French) PC version of Excel 2007 managed to open the sample file in compatibility mode, Russian and Japanese included. There is no telling how this hack would work on other variants, though.
EDIT (bis) : from the file specs linked above:
Byte Strings (BIFF2-BIFF5)
All Excel file formats up to BIFF5 contain simple byte strings. The byte string consists of the length of the string
followed by the character array. The length is stored either as 8bit value or as 16bit value, depending on the current record. The string is not zero-terminated.
The encoding of the character array is dependent on the current record.
Record LABEL, BIFF3-BIFF5:
Offset Size Contents
0 2 Index to row
2 2 Index to column
4 2 Index to XF record
6 var. Byte string, 16-bit string length
Unless you generate a much more complex file, I'm afraid BIFF5 is a no go.
I try to export data out of my Microsoft SQL Server 2008 Database to an Excel Spreadsheet.
I am using this tutorial:
http://www.appservnetwork.com/modules.php?name=News&file=article&sid=8
My header looks like this:
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");;
header("Content-Disposition: attachment;filename=$filename.xls ");
header("Content-Transfer-Encoding: binary ");
The XLS Data Cell looks like this:
xlsBOF();
xlsWriteString(0,0,"$lng_report_age_title");
xlsWriteString(0,4,"$lng_report_sex_title");
xlsWriteString(0,6,"$lng_report_referredfrom_title");
xlsWriteString(0,8,"$lng_report_selfreferred_title");
xlsWriteString(1,0,"$lng_report_age04");
xlsWriteString(1,1,"$lng_report_age514");
xlsWriteString(1,2,"$lng_report_age1549");
xlsWriteString(1,3,"$lng_report_age50");
xlsWriteString(1,4,"$lng_report_sexm");
xlsWriteString(1,5,"$lng_report_sexf");
xlsWriteString(1,6,"$lng_report_referredfrom_indistrict");
xlsWriteString(1,7,"$lng_report_referredfrom_outdistrict");
xlsWriteString(1,8,"$lng_report_selfreferred_indistrict");
xlsWriteString(1,10,"$lng_report_selfreferred_outdistrict");
xlsWriteString(2,6,"$lng_report_referredfrom_new");
xlsWriteString(2,7,"$lng_report_referredfrom_new");
xlsWriteString(2,8,"$lng_report_selfreferred_new");
xlsWriteString(2,9,"$lng_report_selfreferred_old");
xlsWriteString(2,10,"$lng_report_selfreferred_new");
xlsWriteString(2,11,"$lng_report_selfreferred_old");
xlsWriteNumber(3,0,"$searchresultsage04");
xlsWriteNumber(3,1,"$searchresultsage514");
xlsWriteNumber(3,2,"$searchresultsage1549");
xlsWriteNumber(3,3,"$searchresultsage50");
xlsWriteNumber(3,4,"$searchresultssexm");
xlsWriteNumber(3,5,"$searchresultssexf");
xlsWriteNumber(3,6,"$searchresultsreferredfrom01");
xlsWriteNumber(3,7,"$searchresultsreferredfrom02");
xlsWriteNumber(3,8,"$searchresultsselfreferred03");
xlsWriteNumber(3,9,"$searchresultsselfreferred04");
xlsWriteNumber(3,10,"$searchresultsselfreferred05");
xlsWriteNumber(3,11,"$searchresultsselfreferred06");
xlsEOF();
exit();
And the create XLS functions look like this:
function xlsBOF() {
echo pack("ssssss", 0x809, 0x8, 0x0, 0x10, 0x0, 0x0);
return;
}
function xlsEOF() {
echo pack("ss", 0x0A, 0x00);
return;
}
function xlsWriteNumber($Row, $Col, $Value) {
echo pack("sssss", 0x203, 14, $Row, $Col, 0x0);
echo pack("d", $Value);
return;
}
function xlsWriteString( $Row , $Col , $Value ) {
$L = strlen( $Value );
echo pack( "sssss" , 0x204 , 8 + $L , $Row , $Col , 0x0 , $L );
echo $Value;
return;
}
Problem Nr. 1:
In the Excel Sheet there will be several unwanted special characters around the text that I want to display (Maybe because I'm using UTF-8? (tried to utf8_decode the string first; didn't help)Maybe because I'm using Excel 2007?)
Problem Nr. 2:
The Values are not getting written into the row/column I want them to be in.
The result looks like this (the special chars get automatically removed by the stackoverflow editor, so I replace them with Xs):
XXXXXXXAgeXXXXXSexXXXX
Referred FromXXXX
Self ReferredXXX 0-4 YearsXXX
5-14 YearsXXXX 15-49 YearsXXX X
=50 YearsXXX XXMaleXXX XXFemaleXXXX XXIn DistrictXXX XXOut DistrictXXXX XXIn DistrictXXX
XOut DistrictXXXXXXNewXXXXXXNewXXXXXXNewXXXXXOldXXXX
XNewXXXXXXOldXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXX
All this data is in column A and in rows 1-8.
One more point I would like to know, once it works, is:
How can I format the cells (Borders, Fonts, etc)?
Thanks in advance.
As an indirect solution to your question, I'd recommend the classes at http://phpexcel.codeplex.com/.
I've used it in the past with good results. The learning curve isn't too bad, the documentation is OK, it's a small package, it has good support in the forums, and I'm reasonably sure it can answer everything you asked.
I had the same problem.
ob_end_clean(); $objWriter->save('php://output');
This will remove unwanted characters from excel.
I'm using the following code to create an xls file from php.
http://www.appservnetwork.com/modules.php?name=News&file=article&sid=8
However, for some reason if the row "comments" is more than 255 characters, it doesn't output anything..
The code which writes the String to the XLS file is:
function xlsWriteString( $Row, $Col, $Value ) {
$L = strlen( $Value );
echo pack( "ssssss", 0x204, 8 + $L, $Row, $Col, 0x0, $L );
echo $Value;
return;
};
Could someone help me get this to display the field regardless of how many characters are in the string?
Thanks
EDIT: I found this: http://support.microsoft.com/kb/213841 but not sure how to implement the work around into php..
ANOTHER EDIT: Even if anyone knows how to merge cells? That would also work?? :)
Sounds like it may be related to this issue.