PHPExcel使用中遇到的幾個問題
最近做專案,接觸了幾次PHPExcel,過程中遇到了幾個問題,現對PHPExcel的一些常見用法和我遇到的問題及解決方法總結如下。(推薦還是要看一下PHPExcel官網的手冊)。
一、常見用法
這個部分網站上有很多資料了,我這裡不再一一羅列,只是將我用到的幾個地方寫了一下。
<? //-------------------常用方法------------------- //首先要匯入phpexcel require_once ($yourpath.'PHPExcel.php'); //建立phpexcel物件,有兩種方式 1.直接建立 $objPHPExcel = new PHPExcel (); /** 2.通過reader類讀取檔案建立 $objReader = new PHPExcel_Reader_Excel5; //excel2003格式 或 $objReader = new PHPExcel_Reader_Excel2007; //excel2007格式 $readerFile = 'xxx.xls'; if (!$objReader->canRead($readFile)) { echo "無法開啟檔案"; return; } $objPHPExcel = $objReader->load($readerFile); */ //設定屬性 $objPHPExcel->getProperties ()->setCreator ( "Miss Yang" ) ->setLastModifiedBy ( "Miss Yang" ) ->setTitle ( "Office 2003 XLS Document" ) ->setSubject ( "Office 2003 XLS Document" ) ->setDescription ( "TrunkBow" ) ->setKeywords ( "TrunkBow" ) ->setCategory ( "TrunkBow" ); //設定當前的sheet索引,用於後續的內容操作。 //預設情況下,PHPExcel會自動建立第一個sheet被設定SheetIndex=0 $objExcel->setActiveSheetIndex(0); $objActSheet = $objExcel->getActiveSheet(); //設定當前活動sheet的名稱 $objActSheet->setTitle('Sheet1'); //設定單元格內容 $objActSheet->setCellValue('A1', '字串內容'); // 字串內容 $objActSheet->setCellValue('A2', 26); // 數值 $objActSheet->setCellValue('A3', true); // 布林值 $objActSheet->setCellValue('A4', '=SUM(A2:A2)'); // 公式 //顯式指定內容型別 $objActSheet->setCellValueExplicit('A5','8757584',PHPExcel_Cell_DataType::TYPE_STRING); //設定單元格格式 $objActSheet ->getStyle ('A1' ) ->getFont () ->getColor() ->setARGB(PHPExcel_Style_Color::COLOR_RED); $objActSheet ->getStyle ('A1' ) ->getFont () ->setBold(true); $objActSheet ->getStyle ('A') ->getNumberFormat() ->setFormatCode ( PHPExcel_Style_NumberFormat::FORMAT_TEXT ); $objActSheet ->getColumnDimension ('A') ->setWidth(20); $objActSheet ->getColumnDimension ('B') ->setAutoSize(true); $objActSheet ->getColumnDimension('C') ->setVisible(false); //獲取單元格的值 $string = $objActSheet->getCell('A2')->getValue(); //如果單元格內容是公式 $string = $objActSheet->getCell('A2')-> getCalculatedValue(); //生成下拉列表框 $list = "item1,item2,item3"; $objValidation1 = $activeSheet->getCell('A1')->getDataValidation(); $objValidation1->setType( PHPExcel_Cell_DataValidation::TYPE_LIST ) ->setErrorStyle(PHPExcel_Cell_DataValidation::STYLE_INFORMATION ) ->setAllowBlank(false) ->setShowInputMessage(true) ->setShowErrorMessage(true) ->setShowDropDown(true) ->setErrorTitle('輸入的值有誤') ->setError('您輸入的值不在下拉框列表內.') ->setPromptTitle('下拉選擇框') ->setPrompt('請從下拉框中選擇您需要的值!') ->setFormula1('"' . $list . '"'); //下載輸出 $filename = "testphp.xls"; $filename = iconv("utf-8", 'gbk', $filename); $objWriter = PHPExcel_IOFactory::createWriter ( $objPHPExcel, 'Excel5' ); header ( 'Content-Type: application/vnd.ms-excel' ); header ( "Content-Disposition: attachment;filename=$filename" ); header ( 'Cache-Control: max-age=0' ); $objWriter->save ( 'php://output' ); exit(0);
二、問題總結
1.單元格內容為長數字的,輸出時,總是轉成科學計數法。
這個問題網上也有不少資料提到的,大多是說要修改幾個地方的原始碼。但是我一直沒找到對應的原始碼在哪裡,可能是版本不同的原因。我是這樣解決的:
<?
//顯示指定內容型別
$objActSheet->setCellValueExplicit('A5','8757584',PHPExcel_Cell_DataType::TYPE_STRING);
2.PHPExcel佔用記憶體過大。
PHPExcel是一個很強大的處理Excel的PHP開源類,但是很大的一個問題就是它佔用記憶體太大,從1.7.3開始,它支援設定cell的快取方式,但是推薦使用目前穩定的版本1.7.6,因為之前的版本都會不同程度的存在bug。
官網上是這麼說的:PHPExcel平均下來使用1k/單元格的記憶體,因此大的文件會導致記憶體消耗的也很快。單元格快取機制能夠允許PHPExcel將記憶體中的小的單元格物件快取在磁碟或者APC,memcache或者Wincache中,儘管會在讀取資料上消耗一些時間,但是能夠幫助你降低記憶體的消耗。預設情況下,PHPExcel依然將單元格物件儲存在記憶體中,但是你可以自定義。你可以使用PHPExcel_Settings::setCacheStorageMethod()方法,將快取方式作為引數傳遞給這個方法來設定快取的方式。
<? $cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_in_memory; PHPExcel_Settings::setCacheStorageMethod($cacheMethod);
setCacheStorageMethod()方法會返回一個BOOL型變數用於表示是否成功設定(比如,如果APC不能使用的時候,你設定使用APC快取,將會返回false)。每一個worksheet都會有一個獨立的快取,當一個worksheet例項化時,就會根據設定或配置的快取方式來自動建立。一旦你開始讀取一個檔案或者你已經建立了第一個worksheet,就不能在改變快取的方式了。目前,有以下幾種快取方式可以使用:
1). PHPExcel_CachedObjectStorageFactory::cache_in_memory;
預設情況下,如果你不初始化任何快取方式,PHPExcel將使用記憶體快取的方式。
2).PHPExcel_CachedObjectStorageFactory::cache_in_memory_serialized;
使用這種快取方式,單元格會以序列化的方式儲存在記憶體中,這是降低記憶體使用率效能比較高的一種方案。
3). PHPExcel_CachedObjectStorageFactory::cache_in_memory_gzip;
與序列化的方式類似,這種方法在序列化之後,又進行gzip壓縮之後再放入記憶體中,這回跟進一步降低記憶體的使用,但是讀取和寫入時會有一些慢。
4). PHPExcel_CachedObjectStorageFactory::cache_to_discISAM;
當使用cache_to_discISAM這種方式時,所有的單元格將會儲存在一個臨時的磁碟檔案中,只把他們的在檔案中的位置儲存在PHP的記憶體中,這會比任何一種快取在記憶體中的方式都慢,但是能顯著的降低記憶體的使用。臨時磁碟檔案在指令碼執行結束是會自動刪除。
5). PHPExcel_CachedObjectStorageFactory::cache_to_phpTemp;
類似cache_to_discISAM這種方式,使用cache_to_phpTemp時,所有的單元格會還存在php://temp I/O流中,只把他們的位置儲存在PHP的記憶體中。PHP的php://memory包裹器將資料儲存在記憶體中,php://temp的行為類似,但是當儲存的資料大小超過記憶體限制時,會將資料儲存在臨時檔案中,預設的大小是1MB,但是你可以在初始化時修改它:
<?
$cacheMethod = PHPExcel_CachedObjectStorageFactory:: cache_to_phpTemp;
$cacheSettings = array( ' memoryCacheSize ' => '8MB' );
PHPExcel_Settings::setCacheStorageMethod($cacheMethod, $cacheSettings);
php://temp檔案在指令碼結束是會自動刪除。
6). PHPExcel_CachedObjectStorageFactory::cache_to_apc;
當使用cach_to_apc時,單元格儲存在APC中,只在記憶體中儲存索引。APC快取預設超時時間時600秒,對絕大多數應用是足夠了,當然你也可以在初始化時進行修改:
<?
$cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_to_APC;
$cacheSettings = array( 'cacheTime' => 600 );
PHPExcel_Settings::setCacheStorageMethod($cacheMethod, $cacheSettings);
當指令碼執行結束時,所有的資料都會從APC中清楚(忽略快取時間),不能使用此機制作為持久快取。
7).PHPExcel_CachedObjectStorageFactory::cache_to_memcache
使用cache_to_memory時,單元格物件儲存在memcache中,只在記憶體中儲存索引。預設情況下,PHPExcel會在localhost和埠11211尋找memcache服務,超時時間600秒,如果你在其他伺服器或其他埠執行memcache服務,可以在初始化時進行修改:
<?
$cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_to_memcache;
$cacheSettings = array( 'memcacheServer'=> 'localhost',
'memcachePort' => 11211,
'cacheTime' => 600 );
PHPExcel_Settings::setCacheStorageMethod($cacheMethod, $cacheSettings);
從初始化設定的形式上看,MS還不支援多臺memcache伺服器輪詢的方式,比較遺憾。
當指令碼結束時,所有的資料都會從memcache清空(忽略快取時間),不能使用該機制進行持久儲存。
3.下拉列表的資料來源過長
我們還是先來看手冊是怎麼說的:
It is important to remember that any string participating in an Excel formula is allowed to be maximum 255 characters (not bytes).
當下拉列表的資料來源過長(more than 255 characters)時,該下拉列表會顯示不正確(我遇到的情況是:該下拉列表顯示了別的正常的下拉列表的資料來源).
以下是我的解決辦法:
<?
//----------------下拉列表資料來源過長的解決---------------------
//解決下拉框資料來源字串長度過大:將每個來源字串分解到一個空閒的單元格中
$str_list = "item1,item2,item3,......" ;
$str_len = strlen($str_list);
if($str_len>=255){
$str_list_arr = explode(',', $str_list);
if($str_list_arr)
foreach($str_list_arr as $i =>$d){
$c = "P".($i+1);
$activeSheet->setCellValue($c,$d);
}
$endcell = $c;
$activeSheet->getColumnDimension('P')->setVisible(false);
}
$objValidation2 = $activeSheet->getCell("A1")->getDataValidation();
$objValidation2->setType( PHPExcel_Cell_DataValidation::TYPE_LIST )
->setErrorStyle( PHPExcel_Cell_DataValidation::STYLE_INFORMATION )
->setAllowBlank(true)
->setShowInputMessage(true)
->setShowErrorMessage(true)
->setShowDropDown(true)
->setErrorTitle('輸入的值有誤')
->setError('您輸入的值不在下拉框列表內.')
->setPromptTitle('下拉選擇框')
->setPrompt('請從下拉框中選擇您需要的值!');
if($str_len<255)
$objValidation2->setFormula1('"' . $str_list . '"');
else
$objValidation2->setFormula1("sheet1!P1:{$endcell}");
4.本地執行正常,伺服器上PHPExcel不能執行
官網:
The following software is required to develop using PHPExcel:
» PHP version 5.2.0 or newer
» PHP extension php_zip enabled *)
» PHP extension php_xml enabled
» PHP extension php_gd2 enabled (if not compiled in)
另外,請檢查iconv擴充套件是否正常,PHPExcel需要他的支援
5.伺服器上報錯:glob函式找不到
在PHPExcel/Calculation.php 1683行有如下程式碼:
<?
private function __construct() {
$localeFileDirectory = PHPEXCEL_ROOT.'PHPExcel/locale/';
foreach (glob($localeFileDirectory.'/*',GLOB_ONLYDIR) as $filename) {
$filename = substr($filename,strlen($localeFileDirectory)+1);
if ($filename != 'en') {
self::$_validLocaleLanguages[] = $filename;
}
}
$setPrecision = (PHP_INT_SIZE == 4) ? 12 : 16;
$this->_savedPrecision = ini_get('precision');
if ($this->_savedPrecision < $setPrecision) {
ini_set('precision',$setPrecision);
}
}
查一下php手冊不難發現:
Note: This function isn't available on some systems (e.g. old Sun OS).
所以在出錯的伺服器上將glob()函式,替換為opendir函式,替換程式碼為:
<?
private function __construct() {
$localeFileDirectory = PHPEXCEL_ROOT.'PHPExcel/locale/';
$dir = opendir($localeFileDirectory);
while (($filename = readdir($dir)) !== false)
{
if ($filename != 'en') {
self::$_validLocaleLanguages[] = $filename;
}
}
closedir($dir);
$setPrecision = (PHP_INT_SIZE == 4) ? 12 : 16;
$this->_savedPrecision = ini_get('precision');
if ($this->_savedPrecision < $setPrecision) {
ini_set('precision',$setPrecision);
}
}