1. 程式人生 > >PHPExcel使用中遇到的幾個問題

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);
	}
}