使用Phpexcel将phprecord转换为excel非常慢

时间:2018-05-23 11:05:19

标签: php performance codeigniter phpexcel

我使用phpexcel时遇到问题。我必须将记录转换为excel并下载。问题是这个过程很慢。我测试了2000 - 3000条记录,将时间限制设置为0,内存限制设置为256M,但处理和下载需要超过30分钟。我在网上看到其他人可以在10秒内下载大约6000条记录。

请有人帮助我。代码有什么问题?

这是控制器:

function download_aplikasi()
{
    $this->Weblog_m->save_log_m("Download aplikasi");
    $data_aplikasi = $this->Io_excel_m->get_aplikasi_m();
    // Create new PHPExcel object
    $objPHPExcel = new PHPExcel();

    $objPHPExcel->getActiveSheet()->getStyle('1:1')->getFont()->setBold(true);

    // Add some data
    $objPHPExcel->setActiveSheetIndex(0)
        ->setCellValue('A1', 'TGL_APLIKASI')
        ->setCellValue('B1', 'CD_CABANG')
        ->setCellValue('C1', 'NAMA_CABANG')
        ->setCellValue('D1', 'NIP')
        ->setCellValue('E1', 'NAMA_PEGAWAI')
        ->setCellValue('F1', 'JABATAN')
        ->setCellValue('G1', 'NAMA_NASABAH')
        ->setCellValue('H1', 'NO_REK')
        ->setCellValue('I1', 'NO_CIF')
        ->setCellValue('J1', 'DOB_NASABAH')
        ->setCellValue('K1', 'JNS_NASABAH')
        ->setCellValue('L1', 'NOMINAL')
        ->setCellValue('M1', 'ID_NASABAH')
        ->setCellValue('N1', 'PRODUK')
        ->setCellValue('O1', 'JNS_PINJAMAN')
        ->setCellValue('P1', 'JNS_USAHA')
        ->setCellValue('Q1', 'LAMA_USAHA')
        ->setCellValue('R1', 'JNS_JAMINAN')
        ->setCellValue('S1', 'PIC_NASABAH')
        ->setCellValue('T1', 'TELP_NASABAH')
        ->setCellValue('U1', 'NAMA_TOKO')
        ->setCellValue('V1', 'KATEGORI')
        ->setCellValue('W1', 'CATATAN')
        ->setCellValue('X1', 'KETERANGAN')
        ->setCellValue('Y1', 'PIC_UNIT')
        ->setCellValue('Z1', 'TELP_PIC')
        ->setCellValue('AA1', 'KAT_PRODUK')
        ->setCellValue('AB1', 'PROGRESS')
        ->setCellValue('AC1', 'PROGRESS_NOTE');

    $i = 1;
    if (count($data_aplikasi) > 0)
    {
        foreach ($data_aplikasi as $row_aplikasi)
        {
            $data_pic = $this->App_m->detilpic_m($row_aplikasi->idpic);
            $i = $i + 1;
            $objPHPExcel->setActiveSheetIndex(0)
                ->setCellValue('A'.$i, date("d/m/Y", strtotime($row_aplikasi->tglaplikasi)))
                ->setCellValue('B'.$i, $row_aplikasi->cdcabang)
                ->setCellValue('C'.$i, $row_aplikasi->namacabang)
                ->setCellValue('D'.$i, $row_aplikasi->nip)
                ->setCellValue('E'.$i, $row_aplikasi->nama_user)
                ->setCellValue('F'.$i, $row_aplikasi->namajabatan)
                ->setCellValue('G'.$i, $row_aplikasi->nama_nasabah)
                ->setCellValue('H'.$i, $row_aplikasi->norek)
                ->setCellValue('I'.$i, $row_aplikasi->nocif)
                ->setCellValue('J'.$i, $row_aplikasi->tgllahir)
                ->setCellValue('K'.$i, $row_aplikasi->jnsnasabah)
                ->setCellValue('L'.$i, $row_aplikasi->nominal_aplikasi)
                ->setCellValue('M'.$i, $row_aplikasi->ktpnasabah)
                ->setCellValue('N'.$i, $row_aplikasi->produk)
                ->setCellValue('O'.$i, $row_aplikasi->jnspinjaman)
                ->setCellValue('P'.$i, $row_aplikasi->jnsusaha)
                ->setCellValue('Q'.$i, $row_aplikasi->lamausaha)
                ->setCellValue('R'.$i, $row_aplikasi->jnsjaminan)
                ->setCellValue('S'.$i, $row_aplikasi->pic_nasabah)
                ->setCellValue('T'.$i, $row_aplikasi->telpnasabah)
                ->setCellValue('U'.$i, $row_aplikasi->namatoko)
                ->setCellValue('V'.$i, $row_aplikasi->kategori)
                ->setCellValue('W'.$i, $row_aplikasi->catatan)
                ->setCellValue('X'.$i, $row_aplikasi->keterangan)
                ->setCellValue('Y'.$i, $data_pic->nama_user)
                ->setCellValue('Z'.$i, $data_pic->nomorhp)
                ->setCellValue('AA'.$i, $row_aplikasi->namaproduct)
                ->setCellValue('AB'.$i, $row_aplikasi->progress)
                ->setCellValue('AC'.$i, $row_aplikasi->progress_note);
        }
    }
    else $objPHPExcel->setActiveSheetIndex(0)
            ->setCellValue('A2', 'No Record Found');

    // Rename worksheet
    $objPHPExcel->getActiveSheet()->setTitle('Aplikasi');


    // Set active sheet index to the first sheet, so Excel opens this as the first sheet
    $objPHPExcel->setActiveSheetIndex(0);

    header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    header('Content-Disposition: attachment;filename="Aplikasi_'.$this->input->post("bulan_kinerja").'.xlsx"');
    header('Cache-Control: max-age=0');
    header('Cache-Control: max-age=1');
    header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
    header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
    header('Pragma: public'); // HTTP/1.0

    $objWriter = IOFactory::createWriter($objPHPExcel, 'Excel2007');
    $objWriter->save('php://output');
    exit;
}

这是型号:

function get_aplikasi_m()
{
    $periode = $this->input->post("bulan_kinerja");
    $idcabang = $this->input->post("id_cabang");
    $cek_cabang = $this->cek_cabang_m($idcabang);
    $this->db->select('*');
    $this->db->from("tbl_aplikasi");
    $this->db->join("tbl_user", "tbl_user.iduser = tbl_aplikasi.iduser", "Left");
    $this->db->join("tbl_product", "tbl_product.idproduct = tbl_aplikasi.idproduct", "Left");
    $this->db->join("tbl_jabatan", "tbl_user.idjabatan = tbl_jabatan.idjabatan", "Left");
    $this->db->join("tbl_unit", "tbl_product.idunit = tbl_unit.idunit", "Left");
    $this->db->join("tbl_cabang", "tbl_user.idcabang = tbl_cabang.idcabang", "Left");
    $this->db->where("tbl_aplikasi.statusapproval='approve'");
    $this->db->where("MONTH(tbl_aplikasi.tglaplikasi)", date("m", strtotime($periode)));
    $this->db->where("YEAR(tbl_aplikasi.tglaplikasi)", date("Y", strtotime($periode)));

    if ($cek_cabang->jnscabang != "area") $this->db->where("tbl_aplikasi.idcabang", $idcabang);

    $this->db->order_by("tbl_aplikasi.tglaplikasi", "desc");
    return $query = $this->db->get()->result();
}

function getpic_m($idunit)
{
    $this->db->select('*');
    $this->db->from("tbl_user");
    $this->db->join("tbl_jabatan", "tbl_user.idjabatan = tbl_jabatan.idjabatan", "Left");
    $this->db->join("tbl_cabang", "tbl_cabang.idcabang = tbl_user.idcabang", "Left");
    $this->db->where("tbl_user.idunit", $idunit);
    $this->db->where("tbl_user.accstat='active'");
    $this->db->order_by("tbl_user.nama_user", "asc");
    return $query = $this->db->get();
}

1 个答案:

答案 0 :(得分:1)

PHPExcel很旧,没有维护。它已被替换为名为PHPSpreadsheet的新版本,您应该使用该版本。对我来说,它更快,更可靠。

转换起来很容易,因为大部分功能完全相同。不需要进行大量的重写。主要的变化是创建读者,作家,工作簿和工作表对象。

我将继续使用PHPExcel来获得这个答案。

您可以采取一些措施来优化代码。首先,捕获活动工作表对象以供重用,而不是重复调用setActiveSheetIndex()getActiveSheet()

// Create new PHPExcel object
$objPHPExcel = new PHPExcel();
// A new workbook always sets the active sheet index to the first sheet
// Capture the "active sheet" object for easy reuse.
$activeSheet = $objPHPExcel->getActiveSheet();

尽可能尝试使用数组而不是多次调用setCellValue()来设置单元格值块。例如:

// Add some data
$rowArray = ['TGL_APLIKASI', 'CD_CABANG', 'NAMA_CABANG', 'NIP',
    'NAMA_PEGAWAI', 'JABATAN', 'NAMA_NASABAH', 'NO_REK', 'NO_CIF',
    'DOB_NASABAH', 'JNS_NASABAH', 'NOMINAL', 'ID_NASABAH', 'PRODUK',
    'JNS_PINJAMAN', 'JNS_USAHA', 'LAMA_USAHA', 'JNS_JAMINAN',
    'PIC_NASABAH', 'TELP_NASABAH', 'NAMA_TOKO', 'KATEGORI',
    'CATATAN', 'KETERANGAN', 'PIC_UNIT', 'TELP_PIC', 'KAT_PRODUK', 
    'PROGRESS', 'PROGRESS_NOTE'];

//add the array to the activesheet
$activeSheet->fromArray($rowArray, NULL, 'A1');

了解Setting a Range of Cells from an Array

您也可以使用fromArray()添加数据库结果,但需要更改模型。您的模型使用result()返回一个对象数组。如果您返回result_array(),则可以轻松地使用数据通过一次调用应用所有行。

function get_aplikasi_m()的最后一行更改为此

return $this->db->get()->result_array();

但是你有两个并发症。首先是第一列中日期的格式。你可以,并且可能应该修改你的查询,以便在数据检索期间执行此操作。但我不知道如何做到这一点。

第二个复杂因素是添加$data_pic个项目。如果App_m->detilpic_m()返回result_array()会更容易,但我会按原样继续。

从模型数据中添加行和列然后如下所示。

$data_aplikasi = $this->Io_excel_m->get_aplikasi_m();
if (count($data_aplikasi) > 0)
{
    //walk through all records to format date and add pic data
    foreach ($data_aplikasi as $key => $row_aplikasi)
    {
        // format the date and make sure the 
        // main array is updated with the change
        $data_aplikasi[$key]['tglaplikasi'] = date("d/m/Y", strtotime($row_aplikasi['tglaplikasi']));

        $data_pic = $this->App_m->detilpic_m($row_aplikasi['idpic']);
        //make an array from $data_pic values
        $pic_data = [$data_pic->nama_user, $data_pic->nomorhp];
        //get the index where pic data should be inserted
        $index = array_search("keterangan", array_keys($row_aplikasi));
        // insert $pic_data into $row_aplikasi at the right place 
        // and update the model result at the same time
        $data_aplikasi[$key] = array_splice($row_aplikasi, $index, 0, $pic_data);
    }
    //add $data_aplikasi array to the activesheet with one call
    $activeSheet->fromArray($data_aplikasi, NULL, 'A2');
}
else
{
    $activeSheet->setCellValue('A2', 'No Record Found');
} 

我希望评论能够清楚地说明发生了什么。这是整个功能,所以你可以一次看到它。

public function download_aplikasi()
{
    $this->Weblog_m->save_log_m("Download aplikasi");
    // Create new PHPExcel object
    $objPHPExcel = new PHPExcel();
    // A new workbook always sets the active sheet index to the first sheet
    // Capture the "active sheet" object sheet
    $activeSheet = $objPHPExcel->getActiveSheet();

    // Name the worksheet
    $activeSheet->setTitle('Aplikasi');

    $activeSheet->getStyle('1:1')->getFont()->setBold(true);

    // Add some data
    $rowArray = ['TGL_APLIKASI', 'CD_CABANG', 'NAMA_CABANG', 'NIP',
        'NAMA_PEGAWAI', 'JABATAN', 'NAMA_NASABAH', 'NO_REK', 'NO_CIF',
        'DOB_NASABAH', 'JNS_NASABAH', 'NOMINAL', 'ID_NASABAH', 'PRODUK',
        'JNS_PINJAMAN', 'JNS_USAHA', 'LAMA_USAHA', 'JNS_JAMINAN',
        'PIC_NASABAH', 'TELP_NASABAH', 'NAMA_TOKO', 'KATEGORI',
        'CATATAN', 'KETERANGAN', 'PIC_UNIT', 'TELP_PIC', 'KAT_PRODUK',
        'PROGRESS', 'PROGRESS_NOTE'];

    //add the array to the activesheet
    $activeSheet->fromArray($rowArray, NULL, 'A1');


    $data_aplikasi = $this->Io_excel_m->get_aplikasi_m();
    if (count($data_aplikasi) > 0)
    {
        //walk through all records to format date and add pic data
        foreach ($data_aplikasi as $key => $row_aplikasi)
        {
            // format the date and make sure the 
            // main array is updated with the change
            $data_aplikasi[$key]['tglaplikasi'] = date("d/m/Y", strtotime($row_aplikasi['tglaplikasi']));

            $data_pic = $this->App_m->detilpic_m($row_aplikasi['idpic']);
            //make an array from $data_pic values
            $pic_data = [$data_pic->nama_user, $data_pic->nomorhp];
            //get the index where pic data should be inserted
            $index = array_search("keterangan", array_keys($row_aplikasi));
            // insert $pic_data into $row_aplikasi at the right place 
            // and update the model result at the same time
            $data_aplikasi[$key] = array_splice($row_aplikasi, $index, 0, $pic_data);
        }
        //add $data_aplikasi array to the activesheet with one call
        $activeSheet->fromArray($data_aplikasi, NULL, 'A2');
    }
    else
    {
        $activeSheet->setCellValue('A2', 'No Record Found');
    }

    header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    header('Content-Disposition: attachment;filename="Aplikasi_'.$this->input->post("bulan_kinerja").'.xlsx"');
    header('Cache-Control: max-age=0');
    header('Cache-Control: max-age=1');
    header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
    header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
    header('Pragma: public'); // HTTP/1.0

    $objWriter = IOFactory::createWriter($objPHPExcel, 'Excel2007');
    $objWriter->save('php://output');
    //Don't use exit, let CI finish as designed by letting the controller return
    //exit;
}

我假设$this->Io_excel_m->get_aplikasi_m()按照您在原始代码中使用的顺序返回字段。如果字段不是那个顺序,您将不得不重新排列$data_aplikasi或更改查询,以便字段按您想要的顺序排列。就个人而言,我会做第二个。

很难确定,但看起来你可以在调用$data_pic期间修改查询(使用JOIN)来获取get_aplikasi_m()字段。

日期字段也可以您想要的格式从查询中返回。

这两个模型更改将消除用于修改foreach的{​​{1}}循环。执行将更快,因为您删除了数千个数据库调用以获取data_aplikasi。通常,最好不要在循环内部进行查询 - 它需要大量的系统资源和时间。

我没有测试过这段代码(没有数据)所以很可能存在逻辑和语法错误。这个概念是合理的,是我成功使用的一种技术。