【问题标题】:Large number of data export into CSV - codeigniter大量数据导出为 CSV - codeigniter
【发布时间】:2017-11-23 07:02:14
【问题描述】:

所以我有这个 CI 项目,它可以从数据库(包含大量数据)转换为 CSV。

我尝试导出所有数据,点击“转换为 CSV”后,加载需要很长时间,浏览器会给我一个超时错误。

导出函数的代码如下:

        ini_set('memory_limit', '-1');
        set_time_limit(-1);

        $prefKey = $this->session->flashdata('prefKey');
        $searchKey = $this->session->flashdata('searchKey');
        $withEmail = $this->session->flashdata('withEmail');

        $list = $this->user_model->get_users($prefKey, $searchKey, $withEmail, "", "");

        $headerArray = array("id", "prefecture_id", "industry_id", "offset", "name", "email");

        // Header
        $header = str_replace(",", "", $headerArray);
        $datas = implode(',', $header) . "\r\n";

        // Body
        foreach($list as $body)
        {

            $orig_email = $body['email'];

            $mstring = preg_replace("/^([^a-zA-Z0-9])*/",',',$orig_email);

            preg_match_all("/[\._a-zA-Z0-9-]+@[\._a-zA-Z0-9-]+/i", $mstring, $matches);
            $email = implode($matches[0]);

            $datas .= $body["id"].",".$body["prefecture_id"].",".$body["industry_id"].",".$body["offset"].",".preg_replace('/[,]/',' ',$body["name"]).",".$email."\r\n";
        }

        $datas = mb_convert_encoding($datas, "SJIS-win", "UTF-8");

        $csvFileName = "phpList_" . date('Ymd_His') . ".csv";
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename=' . $csvFileName);
        header('Content-Transfer-Encoding: binary');
        while (ob_get_level() > 0)
        {
            ob_end_clean();
        }

        ob_start();
        print trim($datas);
        ob_flush();
        ob_end_clean();
        exit;

php.ini 的设置如下:

max_execution_time = 259200
max_input_time = 259200
memory_limit = 2G

还是一样的错误。

我无法确定它在哪里停止/超时。是从查询数据的时候开始的吗?将数据放在 CSV 中?还是正在下载?

根据上面提供的代码,我该如何做一个分批处理?我认为这只是一个一次性的过程。

这是模型:

public function get_users($prefecture_id, $industry_id, $filter, $limit, $start){ 

        $this->db->select('*'); 
        $this->db->from('company'); 

        if ($filter) {
            $this->db->like('email','@');
        }

        if (!empty($prefecture_id) && $prefecture_id != 99) {
            $this->db->where('prefecture_id', $prefecture_id);
        }

        if (!empty($industry_id) && $industry_id != 99) {
            $this->db->where('industry_id', $industry_id);
        }

        if (!empty($limit)) {

            $this->db->limit($limit, $start); 
        }
        $this->db->order_by('prefecture_id, industry_id, offset');
        $query = $this->db->get(); 

        $result =  $query->result_array(); 

        return $result;
    }

【问题讨论】:

  • 查看php.ini中的max_execution_time,也请开启error_reporting
  • @DavinderKumar 这些设置设置为大。仍然超时。
  • 您的数据库中有多少用户?
  • @sintakonte 用户?你的意思是多少行?
  • 是的 - 我的意思是,请也发布你的模型 - 因为我认为这是问题

标签: php codeigniter csv


【解决方案1】:

您应该为此操作使用 cron 作业。创建一个每分钟运行的 cron 作业 (https://askubuntu.com/questions/2368/how-do-i-set-up-a-cron-job)。 cronjobs 没有超时。

我建议以下功能:

当您按下“生成 CSV”按钮时,您可以将数据库中的标志设置为“真”,并且 cron 作业将开始创建 CSV。在“生成 CSV”按钮附近,您可以创建一个隐藏的“下载 CSV”按钮(最初是隐藏的)。当该过程完成时,您可以将“下载 CSV”设置为可见(您可以使用来自 javascript 的 ajax 调用来检查并查找生成 csv 过程是否已完成)。 cron 作业会将 CSV 创建/保存到预定义的位置,因此下载按钮只会链接(可以是“a href”)到 csv 位置。

如果您正在使用 cron 作业,请确保您有一个数据库标志,例如“is_running”,并且当生成进程正在运行时,此标志设置为 true (tinyint = 1),因此不会启动其他生成 csv 进程。当进程结束时,将标志设置为 false (tinyint = 1)。

其他选项是让服务器每 60/30 分钟调用一次此 cron 作业,并从一开始就显示下载按钮。每次运行 cron 都会更新(删除/创建)同一个 csv 文件。

【讨论】:

    【解决方案2】:

    拥有超过 200 万行 - 您必须确保大多数数据不在内存中。

    这里要做的是unbuffered_row()函数的用法

    您可以在无缓冲行部分here 下的文档中找到此功能

    public function createCSVExportFromCompanyTable($prefecture_id, $industry_id, $filter, $limit, $start)
    {
        set_time_limit(0);
        $this->db->select('*'); 
        $this->db->from('company'); 
    
        if ($filter) {
            $this->db->like('email','@');
        }
    
        if (!empty($prefecture_id) && $prefecture_id != 99) {
            $this->db->where('prefecture_id', $prefecture_id);
        }
    
        if (!empty($industry_id) && $industry_id != 99) {
            $this->db->where('industry_id', $industry_id);
        }
    
        if (!empty($limit)) {
    
            $this->db->limit($limit, $start); 
        }
        $this->db->order_by('prefecture_id, industry_id, offset');
        $query = $this->db->get(); 
    
        $delimiter = ",";
    
        //$f = fopen('php://memory', 'w');
        $f = fopen('exportCompanyData.csv', 'w');
    
        $headerArray = array("id", "prefecture_id", "industry_id", "offset", "name", "email");
    
        fputcsv($f, $headerArray,$delimiter);
    
        while($row = $query->unbuffered_row())
        {
            $orig_email = $row->email;
            $mstring = preg_replace("/^([^a-zA-Z0-9])*/",',',$orig_email);
            preg_match_all("/[\._a-zA-Z0-9-]+@[\._a-zA-Z0-9-]+/i", $mstring, $matches);
            $email = implode($matches[0]);
    
            $arrLine = [
                $row->id, $row->prefecture_id, $row->industry_id, $row->offset, preg_replace('/[,]/',' ',$row->name),$email
            ];
    
            fputcsv($f, $arrLine, $delimiter); 
        }
        fseek($f, 0);
    
        $csvFileName = "phpList_" . date('Ymd_His') . ".csv";
    
        header('Content-Type: application/csv');
        header('Content-Disposition: attachment; filename="'.$csvFileName.'";');
        fpassthru($f);
    
    }
    

    这里唯一的问题可能是$f = fopen('php://memory', 'w'); 由于记忆问题; 如果您仍然面临内存问题,请尝试$f = fopen('exportCompanyData.csv', 'w'); 而是。

    最后一个提示:@First 尝试在最多 1k 行的情况下运行此函数 为了确保功能正常。

    请注意 - 我刚刚写下了这段代码 - 所以我不确定一切在语法上是否正确 - 但路径应该很清楚,你应该能够使用这段代码;)

    【讨论】:

    • 试过$f = fopen('php://memory', 'w');但它仍然超时,也试过$f = fopen('exportCompanyData.csv', 'w');它完成下载但csv是空的。
    • 我现在已经对其进行了测试 - 它就像一个魅力 - 即使有 2 个 mio 项目 - 唯一缺少的东西 - 是将时间限制设置为 0 set_time_limit(0); - 我已经更新我的代码 - 但老实说,我假设你已经知道这样的事情,这感觉就像我应该为你写一个完整的代码,因为你懒得去想那个 sn-p ....(因为如果你有used limit(1000) - 你会看到这段代码有效,但即使这样也要求很高......
    【解决方案3】:

    尝试插入大数据的示例并增加您的 php 超时限制:

    $data = array(
    array( 'item' => 'Server', 'cost' => 10000, 'approved by' => 'Joe'),
    array( 'item' => 'Mt Dew', 'cost' => 1.25, 'approved by' => 'John'),
    array( 'item' => 'IntelliJ IDEA', 'cost' => 500, 'approved by' => 'James')
    );
    
    $fp = fopen('file.csv', 'w');
    $i = 0;
    foreach ($data as $fields) {
     if($i == 0){
        fputcsv($fp, array_keys($fields), 4800);// the number defines the buffer /bytes to be inserted at a time.
     }
      fputcsv($fp, array_values($fields));
      $i++;
    }
    
    fclose($fp);
    

    并更新您的 .htaccess:

    <IfModule mod_rewrite.c> 
    php_value memory_limit 256M 
    </IfModule>
    

    【讨论】:

      猜你喜欢
      • 2016-08-03
      • 2014-09-13
      • 1970-01-01
      • 1970-01-01
      • 2013-06-12
      • 2021-07-11
      • 2017-01-30
      • 2011-12-03
      • 1970-01-01
      相关资源
      最近更新 更多