IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> phpspreadsheet读取大批量数据时内存溢出解决办法 -> 正文阅读

[PHP知识库]phpspreadsheet读取大批量数据时内存溢出解决办法

需求:一次性读取几万条excel数据并保存到数据库中

问题:当excel超过五万条时内存溢出,程序直接停止

解决办法:将表格切割成多个csv文件,每次只读取一部分数据,大大缓解服务器内存压力,缺点是处理时间会变长,适用于对时间要求不是很高的场景。

建议:小于两万五千条时phpspreadsheet直接读取,大于时采取分割法,下边只附分割法的代码

/**
 * 预读过滤类
 * @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 ExcelCutHelper
{
    public $cutNum = 5;
    public $returnType = 'Csv';
    public $fileDir = 'upload/csv/';
    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();
            //大于十万后重新计算分割数,保证每个表最多一万五
            if(array_sum($myFilter->record) >= 100000){
                $this->cutNum = ceil(array_sum($myFilter->record) / 13000);
            }
            $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;
        }
    }
    
    
    
}

调用方法

 //大于两万五分割读取  path为文件存储地址          
 $excel = new ExcelCutHelper();
 $data = $excel->cutFromFile($path);

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 11:48:41  更:2022-04-04 11:49:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 16:02:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码