最近做项目,接触了几次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);
}
}