利用phpspreadsheet可以轻松的解析excel文件,但是phpspreadsheet的内存消耗也是比较大的,我试过解析将近5m的纯文字excel内存使用量就会超过php默认的最大内存128m。
当然这可以用调节内存大小的方法来解决,但是在并发量大的时候就比较危险了。所以今天介绍下一种方法,利用phpspreadsheet对excel文件进行切割,这是个拿时间换空间的方法所以一般对时效性要求低的需求可以使用。
方法:
先放个phpspreadsheet官网提供的一个功能readcell,我们就可以利用这个功能来进行切割。
首先对excel文件进行预读,主要是获取所有的工作表以及工作表下面的数据行数,这个阶段readcell方法一直返回的都是false,我们只需要记录readcell进来的工作表及数据行数。
然后就是对获取到的记录进行分析,确定每部分数据需要装多少行原始excel的数据,需要注意的是为了避免内容混淆,不要将两个工作表的内容切到一起。
最后就是循环分析的数据和再次利用readcell获取每部分数据,注意每次读取文件后都要利用disconnectworksheets方法清理phpspreadsheet的内存。
经过测试发现,利用该方法解析5m的excel文件,平均只需要21m的内存就可以搞定!
代码
<?php
namespace cutexcel;
require_once 'phpspreadsheet/autoload.php';
/
* 预读过滤类
* @author wangyelou
* @date 2018-07-30
*/
class myaheadreadfilter implements \phpoffice\phpspreadsheet\reader\ireadfilter
{
public $record = array();
private $lastrow = '';
public function readcell($column, $row, $worksheetname = '')
{
if (isset($this->record[$worksheetname]) ) {
if ($this->lastrow != $row) {
$this->record[$worksheetname] ++;
$this->lastrow = $row;
}
} else {
$this->record[$worksheetname] = 1;
$this->lastrow = $row;
}
return false;
}
}
/
* 解析过滤类
* @author wangyelou
* @date 2018-07-30
*/
class myreadfilter implements \phpoffice\phpspreadsheet\reader\ireadfilter
{
public $startrow;
public $endrow;
public $worksheetname;
public function readcell($column, $row, $worksheetname = '')
{
if ($worksheetname == $this->worksheetname && $row >= ($this->startrow+1) && $row <= ($this->endrow+1)) {
return true;
}
return false;
}
}
/
* 切割类
* @author wangyelou
* @date 2018-07-30
*/
class excelcut
{
public $cutnum = 5;
public $returntype = 'csv';
public $filedir = '/tmp/';
public $log;
/
* 切割字符串
* @param $str
* @return array|bool
*/
public function cutfromstr($str)
{
try {
$filepath = '/tmp/' . time() . mt_rand(1000, 9000) . $this->returntype;
file_put_contents($filepath, $str);
if (file_exists($filepath)) {
$result = $this->cutfromfile($filepath);
unlink($filepath);
return $result;
} else {
throw new exception('文件写入错误');
}
} catch (exception $e) {
$this->log = $e->getmessage();
return false;
}
}
/
* 切割文件
* @param $file
* @return array|bool
*/
public function cutfromfile($file)
{
try {
$cutrules = $this->readaheadfromfile($file);
$dir = $this->getfiledir($file);
$returntype = $this->returntype ? $this->returntype : 'csv';
$results = array();
//初始化读
$myfilter = new myreadfilter();
$inputfiletype = \phpoffice\phpspreadsheet\iofactory::identify($file);
$reader = \phpoffice\phpspreadsheet\iofactory::createreader($inputfiletype);
$reader->setreaddataonly(true);
$reader->setreadfilter($myfilter);
foreach ($cutrules as $sheetname => $rowindexrange) {
//读
list($myfilter->startrow, $myfilter->endrow, $myfilter->worksheetname) = $rowindexrange;
$spreadsheetreader = $reader->load($file);
$sheetdata = $spreadsheetreader->setactivesheetindexbyname($myfilter->worksheetname)->toarray(null, false, false, false);
$realdatas = array_splice($sheetdata, $myfilter->startrow, ($myfilter->endrow - $myfilter->startrow + 1));
$spreadsheetreader->disconnectworksheets();
unset($sheetdata);
unset($spreadsheetreader);
//写
$savefile = $dir . $sheetname . '.' . $returntype;
$spreadsheetwriter = new \phpoffice\phpspreadsheet\spreadsheet();
foreach ($realdatas as $rowindex => $row) {
foreach ($row as $colindex => $col) {
$spreadsheetwriter->getactivesheet()->setcellvaluebycolumnandrow($colindex+1, $rowindex+1, $col);
}
}
$writer = \phpoffice\phpspreadsheet\iofactory::createwriter($spreadsheetwriter, $returntype);
$writer->save($savefile);
$spreadsheetwriter->disconnectworksheets();
unset($spreadsheetwriter);
$results[] = $savefile;
}
return $results;
} catch (exception $e) {
$this->log = $e->getmessage();
return false;
}
}
/
* 预读文件
*/
public function readaheadfromfile($file)
{
if (file_exists($file)) {
//获取统计数据
$myfilter = new myaheadreadfilter();
$inputfiletype = \phpoffice\phpspreadsheet\iofactory::identify($file);
$reader = \phpoffice\phpspreadsheet\iofactory::createreader($inputfiletype);
$reader->setreaddataonly(true); //只读数据
$reader->setreadfilter($myfilter);
$spreadsheet = $reader->load($file);
//$sheetdata = $spreadsheet->getactivesheet()->toarray(null, false, false, false);
list($filename,) = explode('.', basename($file));
$datas = array();
$averagenum = ceil(array_sum($myfilter->record) / $this->cutnum);
foreach ($myfilter->record as $sheetname => $count) {
for ($i=0; $i<ceil($count/$averagenum); $i++) {
$datas[$filename . '_' . $sheetname . '_' . $i] = array($i*$averagenum, ($i+1)*$averagenum-1, $sheetname);
}
}
return $datas;
} else {
throw new exception($file . ' not exists');
}
}
/
* 创建目录
* @param $file
* @return bool|string
*/
protected function getfiledir($file)
{
$basename = basename($file);
list($name) = explode('.', $basename);
$fullname = $name .'_'. time() . '_' . mt_rand(1000, 9999);
$path = $this->filedir . $fullname . '/';
mkdir($path, 0777);
chmod($path, 0777);
if (is_dir($path)) {
return $path;
} else {
$this->log = mkdir {$path} failed;
return false;
}
}
}
什么是企业云服务器配置域名未来投资趋势:创意域名交易阿里云一个服务器多个域名备案服务器被CC攻击了怎么办中文网址在哪里注册谷歌浏览器scholarscope如何安装 谷歌浏览器安装scholarscope的方法二手电商市场又起硝烟 京东重启拍拍二手品牌免费网址域名使用陷阱和突出问题