我正在使用PHPExcel从Excel文件中读取数据。
使用以下代码,我可以在几秒钟内从 3MB Excel文件中读取一个特定工作表。很好地工作。
但是,我现在有 27MB 和 88MB Excel文件,我需要从中获取数据。它们非常大,即使 OpenOffice 也无法打开它们。
我发现在加载工作表时我可以使用索引号而不是名称,但这似乎不一致,例如在一个特定的Excel文件中setLoadSheetsOnly(0)
给了我第三个表,而setLoadSheetsOnly(1)
给了我错误,即使文件中有四个工作表。因此出于某种原因,似乎不可靠。
有没有办法可以从大文件中读出工作表的名称,这样我一次只能访问其中一个工作表?
$objReader = PHPExcel_IOFactory::createReaderForFile("data/" . $file_name);
$objReader->setLoadSheetsOnly(array($sheet_name));
$objReader->setReadDataOnly(true);
$objPHPExcel = $objReader->load("data/" . $file_name);
echo '<table border="1">';
for ($row = 1; $row < $number_of_rows; $row++) {
echo '<tr>';
for ($column = 0; $column < $number_of_columns; $column++) {
$value = $objPHPExcel->setActiveSheetIndex(0)->getCellByColumnAndRow($column, $row)->getValue();
echo '<td>';
echo $value . ' ';
echo '</td>';
}
echo '</tr>';
}
echo '</table>';
die;
我找到了一些接近的代码,但它似乎并不总是准确的,例如这里错过了27MB文件中的第二个工作表:
在这里它只获得了第三张工作表而错过了其他3张:
$objReader = PHPExcel_IOFactory::createReaderForFile("data/" . $file_name);
$objReader->setLoadSheetsOnly(0);
$objReader->setReadDataOnly(true);
$objPHPExcel = $objReader->load("data/" . $file_name);
echo $objPHPExcel->getSheetCount(), ' worksheets<hr/>';
$loadedSheetNames = $objPHPExcel->getSheetNames();
foreach ($loadedSheetNames as $sheetIndex => $loadedSheetName) {
echo $sheetIndex, ' -> ', $loadedSheetName, '<br />';
}
die;
答案 0 :(得分:3)
不幸的是,如果不加载整个文件,就无法读取工作表的名称。
调用setLoadSheetsOnly()时使用索引号而不是名称将不会产生可预测的结果:执行该检查的代码逻辑使用in_array()来测试它将要读取的表名是否在数组中要读取的片材名称。 e.g。
// check if sheet should be skipped
if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) {
continue;
}
我怀疑在执行此测试时(基于PHP的松散类型和比较转换规则),字符串与数值的比较将给出0 ==“mySheetName”的真实结果。
我可能会提供一个Reader方法,它会返回一个工作表名称列表而不会实际加载整个文件,尽管会涉及性能损失。
修改强>
如果将以下方法添加到Classes / PHPExcel / Reader / Excel2007.php
/**
* Reads names of the worksheets from a file, without loading the whole file to a PHPExcel object
*
* @param string $pFilename
* @throws Exception
*/
public function listWorksheetNames($pFilename)
{
// Check if file exists
if (!file_exists($pFilename)) {
throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
}
$worksheetNames = array();
$zip = new ZipArchive;
$zip->open($pFilename);
$rels = simplexml_load_string($this->_getFromZipArchive($zip, "_rels/.rels")); //~ http://schemas.openxmlformats.org/package/2006/relationships");
foreach ($rels->Relationship as $rel) {
switch ($rel["Type"]) {
case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument":
$xmlWorkbook = simplexml_load_string($this->_getFromZipArchive($zip, "{$rel['Target']}")); //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main");
if ($xmlWorkbook->sheets) {
foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
// Check if sheet should be skipped
$worksheetNames[] = (string) $eleSheet["name"];
}
}
}
}
$zip->close();
return $worksheetNames;
}
您可以使用以下方式调用它:
$inputFileType = 'Excel2007';
$inputFileName = 'biostat-behfisk-2005.xlsx';
$objReader = PHPExcel_IOFactory::createReader($inputFileType);
$worksheetNames = $objReader->listWorksheetNames($inputFileName);
foreach ($worksheetNames as $sheetName) {
echo $sheetName, '<br />';
}
返回的$ worksheetNames应包含所有工作表名称的数组作为UTF-8字符串。因为它只是从.xlsx读取绝对最小值来检索这些名称,所以它应该相当快。在将其检入PHPExcel SVN之前,我会做更多测试,但(现在)它似乎可以满足您的需求。
<强> EDIT2 强>
Excel5阅读器的等效方法
/**
* Reads names of the worksheets from a file, without loading the whole file to a PHPExcel object
*
* @param string $pFilename
* @throws Exception
*/
public function listWorksheetNames($pFilename)
{
// Check if file exists
if (!file_exists($pFilename)) {
throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
}
$worksheetNames = array();
// Read the OLE file
$this->_loadOLE($pFilename);
// total byte size of Excel data (workbook global substream + sheet substreams)
$this->_dataSize = strlen($this->_data);
$this->_pos = 0;
$this->_sheets = array();
// Parse Workbook Global Substream
while ($this->_pos < $this->_dataSize) {
$code = self::_GetInt2d($this->_data, $this->_pos);
switch ($code) {
case self::XLS_Type_BOF: $this->_readBof(); break;
case self::XLS_Type_SHEET: $this->_readSheet(); break;
case self::XLS_Type_EOF: $this->_readDefault(); break 2;
default: $this->_readDefault(); break;
}
}
foreach ($this->_sheets as $sheet) {
if ($sheet['sheetType'] != 0x00) {
// 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
continue;
}
$worksheetNames[] = $sheet['name'];
}
return $worksheetNames;
}
不如Excel2007 Reader版本那么高效,但仍然比仅仅为表格名称解析整个.xls文件更快,因为我只解析全局流。
答案 1 :(得分:2)
我不想修改phpexcel,所以我选择了这个:
public function getWorksheetNames($pFilename) {
$worksheetNames = array ();
$zip = zip_open ( $pFilename );
while ( $entry = zip_read ( $zip ) ) {
$entry_name = zip_entry_name ( $entry );
if ($entry_name == 'xl/workbook.xml') {
if (zip_entry_open ( $zip, $entry, "r" )) {
$buf = zip_entry_read ( $entry, zip_entry_filesize ( $entry ) );
$workbook = simplexml_load_string ( $buf );
foreach ( $workbook->sheets as $sheets ) {
foreach( $sheets as $sheet) {
$attributes=$sheet->attributes();
$worksheetNames[]=$attributes['name'];
}
}
zip_entry_close ( $entry );
}
break;
}
}
zip_close ( $zip );
return $worksheetNames;
}
它仅适用于excel 2007或更高版本但是我需要的是