PhpSpreadsheet is corrupting files - excel

I am using PhpSpreadsheet to modify an existing file and send it to the browser, but every time I download the file excel gives me the following error:
We found a problem with some content in filename.xlsx. Do you want us to try and recover as much as we can? If you trust the source of this workbook, click Yes.
I have stripped back everything to the following code. The template file that I am opening is a brand new excel file, with no edits made to it (to avoid the potential that the error already exists in the template). I can open this file from the drive without any issues.
$spreadsheet = IOFactory::load(storage_path() ."\Template - English.xlsx");
// Redirect output to a client’s web browser (Xlsx)
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="filename.xlsx"');
header('Cache-Control: max-age=0');
// If you're serving to IE 9, then the following may be needed
header('Cache-Control: max-age=1');
// If you're serving to IE over SSL, then the following may be needed
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
header('Pragma: public'); // HTTP/1.0
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save('php://output');
Once I go through the repair process I get the following message from Excel, and everything seems to work fine.
Excel completed file level validation and repair. Some parts of this workbook may have been repaired or discarded.
**EDIT: **
The same error occurs when I generate a new file using $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();

I don't know if you solved your issue but I had the same.
My code looks like this :
$strFilename = sprintf('%s_%s_subscriptions', date('Y-m-d-H-i'), $alias);
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="'.$strFilename.'.xlsx"');
header('Cache-Control: max-age=0');
$writer = IOFactory::createWriter($objSpreadsheet, 'Xlsx');
$writer->save('php://output');
And Excel prompt the same error as you. But it looks like PHPSpreadSheet create a buffer and doesn't close it once you save the spreadsheet.
By adding a "die;" after the final line, it solved the issue...
So final code :
$strFilename = sprintf('%s_%s_subscriptions', date('Y-m-d-H-i'), $alias);
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="'.$strFilename.'.xlsx"');
header('Cache-Control: max-age=0');
$writer = IOFactory::createWriter($objSpreadsheet, 'Xlsx');
$writer->save('php://output');
die;
Hope it helps !

For me, the problem was generated by the dump () function, which added debug data to "php: // output" during export, the data was interpreted by PHPSpreadshet as part of the excel file
After removing everything worked

Just use exit() at the end.
ob_end_clean();
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save('php://output');
exit();

Related

Header menu included in exported excel sheet in wordpress front pages

$now = gmdate('D, d M Y H:i:s') . ' GMT';
$filename ="analysis_report.xls";
header('Content-type: application/ms-excel');
header('Expires: ' . $now);
header('Content-Disposition: attachment; filename='.$filename);
header('Pragma: no-cache');
require(SB_PATH."views/export_analysis/analysis_report_export.php");
exit;
I have to export dynamic html table data in wordpress front users data comes fine but header menu also get included in excel sheet
Same code working fine in wordpress backend.
Here is screenshot
https://prnt.sc/qyw9v6
Please suggest to correct this.
Your question is a little vague... but the answer to why you are seeing the csv in every file is because you're including it after headers are sent. This means you need to hook your function into init or admin_init as the case may be.
Also.. You want to only fire the csv export function when the button is clicked. What I do is something like this.
// CSV Export
if (isset($_REQUEST['do']) && $_REQUEST['do'] == 'download' && $_GET['page'] == 'your-page-slug'){
add_action('admin_init', 'download_csv_file');
}
I fire the csv export with a button on the admin page like this.
<a class="button-primary" href="<?php echo admin_url('admin.php?page=your-page-slug&do=download');?>&_wpnonce=<?php echo wp_create_nonce( 'download_csv' )?>">Download Existing Data</a>
I believe with the information above, you should be able to solve your problem.

Content cannot be enabled for .xlsm file generated with PhpExcel

I am trying to load and write contents on already existing .xlsm templates and save it as a new file in different directory.These files has macros ad the macros needs to be protected as it were in the original file. I have done the following things in php to get the result :
<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
require_once dirname(__FILE__) . '/php_excel/Classes/PHPExcel.php';
require_once dirname(__FILE__) . '/php_excel/Classes/PHPExcel/IOFactory.php';
require_once dirname(__FILE__) . '/php_excel/Classes/PHPExcel/Reader/IReadFilter.php';
date_default_timezone_set('Europe/London');
$sheets = array('Welcome', 'Instructions', 'Data Definitions', 'Clothing');
$inputFileName = __DIR__ . '/templates/Clothing.xlsm';
$inputFileType = PHPExcel_IOFactory::identify($inputFileName);
$this->PHPExcelReader = PHPExcel_IOFactory::createReader($inputFileType);
$sheetnames = $sheets;
$this->PHPExcelReader->setLoadSheetsOnly($sheetnames);
$this->PHPExcel = $this->PHPExcelReader->load($inputFileName);
$this->PHPExcel->setActiveSheetIndex(3);
// Redirect output to a client’s web browser (Excel2007)
ob_end_clean();
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="test.xlsm"');
header('Cache-Control: max-age=0');
$objWriter = PHPExcel_IOFactory::createWriter($this->PHPExcel, 'Excel2007');
ob_end_clean();
$objWriter->save($this->filename);
$this->PHPExcel->disconnectWorksheets();
unset($this->PHPExcel);
$this->filename is available in my class.
Everything works fine. I get exactly the same content as the original file but while enabling content i get
Runtime error '9':
Subscript out of range.
Cloned File |
Original File
I saw number of posts on github and wiki but didn't find what i needed. This was some how close but it had no answer or it just said phpexcel doesn't support xlsm file. If so , what is the best library that would support xlsm and macros things ? I have stucked in this since 3 days.
Any kinds of suggestion and help are really appreciated.
Thanks.

Groovy/Grails document download

I'm currently working on a web application using grails. One of the requirements is to generate excel timesheets and download it afterword.
This is my code for downloading from grails controller.
response.contentType = "application/vnd.ms-excel"
response.setHeader("Content-Disposition","attachment;filename=name.xls")
response.outputStream << wb.bytes
response.outputStream.flush()
But my excel file is corrupted. I can open it using open office, but doesn't work using microsoft office or google drive. Looks like the content of the xls file is not well formatted.
If I save document instead of downloading everything is ok.
FileOutputStream fileOut = new FileOutputStream("name.xls")
wb.write(fileOut)
fileOut.close()
I cannot figured out why the file content is corrupted when downloaded as byte array.
Grails version - 2.3.7
Apache poi version - 3.13
Thanks in advance,
Method code
def generate(){
TimeSheetExportWrapper timeSheet = new TimeSheetExportWrapper()
bindData(timeSheet, params.ts)
HSSFWorkbook wb = excelExportService.createExcelTimeSheet(getCurrentTenant(), timeSheet, getCurrentTimezone())
response.contentType = "application/vnd.ms-excel"
response.setHeader("Content-Disposition", "attachment;filename=${timeSheet.proposedFileName}")
response.outputStream << wb.bytes
response.outputStream.flush()
}
There are a few things that you should be doing:
First, set the content length: response.setHeader("Content-Length", "${wb.bytes.length}")
Secondly, close the output: response.outputStream.close()
And finally, make sure you return null to ensure Grails does not attempt to render a view.
def generate(){
TimeSheetExportWrapper timeSheet = new TimeSheetExportWrapper()
bindData(timeSheet, params.ts)
HSSFWorkbook wb = excelExportService.createExcelTimeSheet(getCurrentTenant(), timeSheet, getCurrentTimezone())
response.contentType = "application/vnd.ms-excel"
response.setHeader("Content-Length", "${wb.bytes.length}")
response.setHeader("Content-Disposition", "attachment;filename=${timeSheet.proposedFileName}")
response.outputStream << wb.bytes
response.outputStream.flush()
response.outputStream.close()
return null
}

Excel Export Blank Sheet Problem

I've had some code that has worked for years to export an html table to Excel. It goes like this-
private void ExcelExport ( string core_number )
{
// set response up for excel export
Response.Clear ();
Response.Buffer = true;
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader ( "content-disposition", "attachment;filename=TEST.xls" );
.. loops through generates an simple html table ...
Response.Write ( "</table>" );
Response.Flush ();
Response.End();
}
I noticed Firefox now shows the file as an XML document instead of a Excel document, and when I open the generated document it will parse fail. Also some users report having export problems in some older versions of IE, even though I'm not having any problems in IE on my end (shows as an Excel document and opens properly).
Anyone seen this one before? Thanks for your time.
didn't find an answer, but worked around it for now.

How to work around the [1] IE bug while saving an excel file from a Web server?

I've noticed that Internet Explorer adds a number in square brackets to files downloaded from the internet (usually [1]). This creates a big problem with downloading Excel spreadsheets as square brackets are not a valid filename character inside Excel worksheet name. That problem is IE specific, others browsers are keeping same file name.
So, if you have a pivot table auto-refreshed on file opening for example, you'll get an error message saying the name "file[1].yourPivotTableName" is not valid.
Is there any solution to that problem ?
EDIT : It seems that whatever the filename suggested by HTTP directives, IE adds [1] in all cases, which cause the problem ! (So, answers about filenames aren't helpful in that case)
EDIT : I've tried some VBA code to save file under another name when it'll open. However, it doesn't work (same error message than before). Do you think there's a way to fix that with VBA ?
I've got it working using VBA provided by this cool guy (think of him fondly).
It renames the file and then reattaches the pivots.
http://php.kennedydatasolutions.com/blog/2008/02/05/internet-explorer-breaks-excel-pivot-tables/
I think that this happens when you open the spreadsheet in IE and IE saves it to a temporary file. And I think it only happens when the spreadsheet's filename has more than one dot in it. Try it with a simple "sample.xls".
Another workaround is to tell users to save the file to the desktop and then open it.
It's a built-in feature in Internet Explorer.
Stop using "Open", start using "Save" in the file-download window, otherwise IE will append "[1]" to filename of the file that it places in some temporary folder.
You could build some .NET application using System.IO.FileSystemWatcher that catches the event of the creation of the downloaded file or something and renames the file.
I have solved this issue by using method where we pass 3 parameters: Filename, file extension(without the .dot) and the HTTP request); then doing the UTF-8 encoding of the filename and extension.
Sample Code:
public static String encoding(String fileName, String extension, HttpServletRequest request)
{
String user = request.getHeader( "user-agent" );
boolean isInternetExplorer = ( user.indexOf( "MSIE" ) > -1 );
String var = "";
try
{
fileName = URLEncoder.encode( fileName, "UTF-8" );
fileName = fileName.trim().replaceAll( "\\+", " " );
extension = URLEncoder.encode( extension, "UTF-8" );
extension = extension.trim().replaceAll( "\\+", " " );
if ( isInternetExplorer )
{
disposition = "attachment; filename=\"" + fileName+"."+extension+"\"";
}
else
{
var = "attachment; filename*=UTF-8''" + fileName+"."+extension;
}
}
catch ( UnsupportedEncodingException ence )
{
var = "attachment; filename=\"" + fileName+"."+extension;
ence.printStackTrace();
}
return var;
}
This worked just fine in my case.
Hope it will help you all.
Actually, the correct .NET-code is as following:
Response.AppendHeader("content-disposition", "attachment;filename=file.xls");
Response.ContentType = "application/vnd.ms-excel";
Note: AppendHeader, not AddHeader, which I think only works in debug web-server and IIS7.
The following has worked for me:
private string EncodeFileName(string fileName)
{
fileName = HttpUtility.UrlEncode(fileName, Encoding.UTF8).Replace("+", " ");
if (HttpContext.Current.Request.UserAgent.ToLower().Contains("msie"))
{
var res = new StringBuilder();
var chArr = fileName.ToCharArray();
for (var j = 0; j < chArr.Length; j++)
{
if (chArr[j] == '.' && j != fileName.LastIndexOf("."))
res.Append("%2E");
else
res.Append(chArr[j]);
}
fileName = res.ToString();
}
return "\"" + fileName + "\"";
}
You could just make sure that in the options box for the pivot the auto refresh is switched off. Now even when opened from the server the pivot will work perfectly
I have encountered the same problem and came up with (imo) a better solution that does not need any VBA.
If you set "Content-Disposition" header to "attachment; filename=<...>" instead of "inline; filename=<...>" the normal browsers will open dialog that will allow to save or open a file with a filename defined in a header, but Internet Explorer will behave in kind of weird way. It will open file download dialog and if you press Save it will suggest a filename that is defined in the header, but if you press Open it will save file to a temporary folder and open it with a name that is the same as your URN (without 'namespace'), e.g. if your URI is http://server/folder/file.html, so IE will save your file as file.html (no brackets, woo hoo!). This leads us to a solution:
Write a script that handles request from http://server/folder/* and when you need to serve an XLS file just redirect to that script (use your filename instead of asterisk) with Content-Disposition set to inline.
Put these four lines in your code:
response.reset();
response.setHeader("Expires", "0");
response.setHeader("Cache-Control","must-revalidate,post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
Hope this helps.
In .NET I have found from experience only this seems to work for me:
Response.AddHeader("Content-Disposition", "attachment; filename=excel.xls");
Response.AddHeader("Content-Type", "application/vnd.ms-excel");
Response.ContentType = "application/vnd.ms-excel";
The duplication smells, but so far I have never got to the bottom of it (maybe Sebs post explains this). Also the "content-Disposition" value appears very finicky use a : instead of a ; or ommit the space between it and 'filename' and it blows!
Also if you have compression enabled on IIS this may fix things for you:
Response.ClearHeaders()

Resources