大量数据导出到CSV - codeigniter

时间:2017-11-23 07:02:15

标签: php codeigniter csv

所以我将这个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;
    }

3 个答案:

答案 0 :(得分:1)

超过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行运行此函数   为了确保功能正常。

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

答案 1 :(得分:1)

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

我建议以下功能:

当您按下"生成CSV"按钮你可以在数据库中设置一个标志为" true"并且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 :(得分:0)

尝试插入大数据的示例并增加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>