在 Web 系統中「一鍵匯出 Excel」是常見需求。本文以 ThinkPHP 3.x + PHPExcel 為例,示範從查詢資料、設定欄位標題、填入資料列、輸出下載的完整流程,並說明常見編碼與記憶體優化技巧。
Vendor 載入)public function input() {
// 1. 清除輸出緩衝,避免亂碼
ob_end_clean();
// 2. 設定標頭編碼
header("Content-Type: text/html; charset=utf-8");
// 3. 載入 PHPExcel 核心類別 (ThinkPHP Vendor 機制)
Vendor('Excel.PHPExcel.Writer.IWriter');
Vendor('Excel.PHPExcel.Writer.Excel5');
Vendor('Excel.PHPExcel');
Vendor('Excel.PHPExcel.IOFactory');
// 4. 建立試算表物件
$objPHPExcel = new PHPExcel();
$sheet = $objPHPExcel->getActiveSheet();
// 5. 設定欄位標題 (A1 ~ AL1)
$titles = [
'A1'=>'ID', 'B1'=>'Studentname', 'C1'=>'IDcard', 'D1'=>'StudentID',
'E1'=>'ClassID', 'F1'=>'Sex', 'G1'=>'Nation', 'H1'=>'Birth',
'I1'=>'Imagepath', 'J1'=>'Huji', 'K1'=>'Phone', 'L1'=>'QQ',
'M1'=>'Email', 'N1'=>'Addressnow', 'O1'=>'Local', 'P1'=>'Position',
'Q1'=>'Macrosoft', 'R1'=>'CoachID', 'S1'=>'CarID', 'T1'=>'CartypeID',
'U1'=>'Introducer', 'V1'=>'Signdate', 'W1'=>'Signplace', 'X1'=>'Signgtype',
'Y1'=>'Lubiao', 'Z1'=>'Lubiaodate', 'AA1'=>'Zhika', 'AB1'=>'Zhikadate',
'AC1'=>'Subtabdate', 'AD1'=>'Begindate', 'AE1'=>'Bigtabdate',
'AF1'=>'Weier', 'AG1'=>'Lixiao', 'AH1'=>'Fingerprint',
'AI1'=>'Inform', 'AJ1'=>'SchoolID', 'AK1'=>'Setting', 'AL1'=>'Remarks'
];
foreach ($titles as $cell => $value) {
$sheet->setCellValue($cell, $value);
}
// 6. 組合查詢條件
$where['Lixiao'] = '否';
$where['SchoolID'] = $_SESSION['SchoolID'];
$studentname = I('Studentname');
$idcard = I('IDcard');
if ($studentname) $where['Studentname'] = $studentname;
if ($idcard) $where['IDcard'] = $idcard;
$where['_logic'] = 'AND';
// 7. 查詢資料
$data = M('studentinfo')->where($where)->select();
$count = count($data);
// 8. 填入資料列 (從第 2 列開始)
$row = 2;
foreach ($data as $record) {
$sheet->setCellValue('A'.$row, $record['ID'])
->setCellValue('B'.$row, $record['Studentname'])
->setCellValue('C'.$row, $record['IDcard'])
->setCellValue('D'.$row, $record['StudentID'])
->setCellValue('E'.$row, $record['ClassID'])
->setCellValue('F'.$row, $record['Sex'])
->setCellValue('G'.$row, $record['Nation'])
->setCellValue('H'.$row, $record['Birth'])
->setCellValue('I'.$row, $record['Imagepath'])
->setCellValue('J'.$row, $record['Huji']) // 注意:原碼誤寫 G,這裡修正為 J
->setCellValue('K'.$row, $record['Phone'])
->setCellValue('L'.$row, $record['QQ'])
->setCellValue('M'.$row, $record['Email'])
->setCellValue('N'.$row, $record['Addressnow'])
->setCellValue('O'.$row, $record['Local'])
->setCellValue('P'.$row, $record['Position'])
->setCellValue('Q'.$row, $record['Macrosoft'])
->setCellValue('R'.$row, $record['CoachID'])
->setCellValue('S'.$row, $record['CarID'])
->setCellValue('T'.$row, $record['CartypeID'])
->setCellValue('U'.$row, $record['Introducer'])
->setCellValue('V'.$row, $record['Signdate'])
->setCellValue('W'.$row, $record['Signpalce']) // 原碼拼字 Signpalce
->setCellValue('X'.$row, $record['Signgtype'])
->setCellValue('Y'.$row, $record['Lubiao'])
->setCellValue('Z'.$row, $record['Lubiaodate'])
->setCellValue('AA'.$row, $record['Zhika'])
->setCellValue('AB'.$row, $record['Zhikadate'])
->setCellValue('AC'.$row, $record['Subtabdate'])
->setCellValue('AD'.$row, $record['Begindate'])
->setCellValue('AE'.$row, $record['Bigtabdate'])
->setCellValue('AF'.$row, $record['Weier'])
->setCellValue('AG'.$row, $record['Lixiao'])
->setCellValue('AH'.$row, $record['Fingerprint'])
->setCellValue('AI'.$row, $record['Inform'])
->setCellValue('AJ'.$row, $record['SchoolID'])
->setCellValue('AK'.$row, $record['Setting'])
->setCellValue('AL'.$row, $record['Remarks']);
$row++;
}
// 9. 設定工作表標題
$sheet->setTitle('studentinfo');
$objPHPExcel->setActiveSheetIndex(0);
// 10. 輸出 HTTP 標頭並送出檔案
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment; filename="studentinfo.xls"');
header('Cache-Control: max-age=0');
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
$objWriter->save('php://output');
exit; // 確保腳本結束
}
<div style="float:right">
<form action="{:U('input')}" method="post">
<input type="submit" value="一鍵導出 EXCEL" />
</form>
</div>
PHPExcel_CachedObjectStorageFactory::cache_to_phpTemp 或 cache_to_diskISAM 降低記憶體佔用。urlencode 或 RFC 5987 格式:header('Content-Disposition: attachment; filename*=UTF-8\'\'' . urlencode('學生名單.xls'));原始碼中 Huji 寫入 G 欄導致覆蓋 Nation,已修正為 J 欄;Signpalce 疑為拼字錯誤,保留原資料庫欄位名稱。
PHPExcel 已停止維護 (2017),新專案建議改用 PhpSpreadsheet (支援 .xlsx、效能更好、PHP 7.1+)。
composer require phpoffice/phpspreadsheet
超過 5 萬列或 10 MB 建議改用 fputcsv 串流輸出 CSV,或使用 PhpSpreadsheet\Writer\Xlsx 搭配 setPreCalculateFormulas(false)。