首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 導入導出 CSV 完整實戰:編碼處理、批量寫入與大數據優化

     編輯:PHP 綜合知識

CSV(逗號分隔值)是系統間資料交換最通用的格式。本文提供一個完整的 PHP + MySQL 導入/導出 CSV 實作,涵蓋編碼轉換(UTF-8 ↔ GB2312)、fgetcsv 解析、批量 INSERT 優化、HTTP 下載頭設定,並針對大數據量場景給出效能建議。

資料表結構

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `sex` varchar(10) NOT NULL,
  `age` smallint(3) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

前端介面

<form id="addform" action="do.php?action=import" method="post" enctype="multipart/form-data">
  <p>請選擇要導入的 CSV 檔案:<br/>
  <input type="file" name="file">
  <input type="submit" class="btn" value="導入 CSV">
  <input type="button" class="btn" value="導出 CSV" onclick="window.location.href='do.php?action=export'"></p>
</form>

核心處理邏輯 (do.php)

<?php
include_once "connect.php"; // PDO 連線
$action = $_GET['action'] ?? '';

if ($action === 'import') {
    // ---- 導入 CSV ----
    $filename = $_FILES['file']['tmp_name'] ?? '';
    if (!$filename) { exit('請選擇 CSV 檔案!'); }

    $handle = fopen($filename, 'r');
    if (!$handle) { exit('無法開啟檔案!'); }

    $rows = [];
    while (($data = fgetcsv($handle, 10000)) !== FALSE) {
        $rows[] = $data;
    }
    fclose($handle);

    if (count($rows) <= 1) { exit('無有效資料!'); }

    // 跳過標題列,批量構建 VALUES
    $values = [];
    for ($i = 1; $i < count($rows); $i++) {
        $name = iconv('GB2312', 'UTF-8', $rows[$i][0] ?? '');
        $sex  = iconv('GB2312', 'UTF-8', $rows[$i][1] ?? '');
        $age  = (int)($rows[$i][2] ?? 0);
        $values[] = "('$name','$sex','$age')";
    }

    $sql = "INSERT INTO student (name, sex, age) VALUES " . implode(',', $values);
    $affected = $pdo->exec($sql);
    echo $affected ? "導入成功,共 $affected 筆" : "導入失敗:" . implode('; ', $pdo->errorInfo());
}
elseif ($action === 'export') {
    // ---- 導出 CSV ----
    $stmt = $pdo->query("SELECT name, sex, age FROM student ORDER BY id");
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // 標題列 + 內容,轉 GB2312 相容 Excel 中文
    $output = fopen('php://temp', 'r+');
    fputcsv($output, ['姓名', '性別', '年齡']); // 寫入標題
    foreach ($rows as $row) {
        fputcsv($output, [
            iconv('UTF-8', 'GB2312//IGNORE', $row['name']),
            iconv('UTF-8', 'GB2312//IGNORE', $row['sex']),
            $row['age']
        ]);
    }
    rewind($output);
    $csvContent = stream_get_contents($output);
    fclose($output);

    $filename = date('Ymd') . '_students.csv';
    header('Content-Type: text/csv; charset=GB2312');
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    echo $csvContent;
}

關鍵技術點深度解析

環節要點最佳實踐
編碼轉換中文 Excel 偏好 GB2312/GBKiconv('UTF-8', 'GB2312//IGNORE', $str) 導出;iconv('GB2312', 'UTF-8', $str) 導入。//IGNORE 忽略無法轉換字元。
CSV 解析fgetcsv 自動處理引號、逗號、換行設定足夠長度 fgetcsv($h, 10000);PHP 8+ 可用 fgetcsv($h, 0, ',', '"') 明確指定分隔符。
批量寫入單次 INSERT ... VALUES (...),(...)每批 500-1000 列,避免 SQL 過長(max_allowed_packet)。大量資料分批提交事務。
記憶體控制導出大量資料使用 php://temp 暫存檔 + fputcsv 流式寫入,避免一次性佔用大量記憶體。
下載頭觸發瀏覽器「另存為」Content-Disposition: attachment; filename="..." + Content-Type: text/csv;加 charset 避免瀏覽器誤判編碼。

大數據量優化方案(>10 萬列)

  1. 導入:LOAD DATA LOCAL INFILE 直接由 MySQL 讀取 CSV,速度比 PHP 批量 INSERT 快 10-50 倍。需確認 local_infile=1 且 PHP PDO 啟用 MYSQL_ATTR_LOCAL_INFILE。
  2. 導出:SELECT ... INTO OUTFILE '/tmp/export.csv' 由 MySQL 直接寫檔,再用 readfile() 或 Nginx X-Accel-Redirect 下發。
  3. 分塊處理:PHP 端使用 LIMIT offset, chunk 迴圈,配合 set_time_limit(0)、ignore_user_abort(true)。
  4. 非同步佇列:導入/導出任務推入 Redis/RabbitMQ,Worker 背景執行,前端輪詢進度。

常見坑與避雷

  • BOM 頭導致導入首列亂碼:用 fopen($f, 'r') 讀取時若檔案含 UTF-8 BOM,首欄會帶 \xEF\xBB\xBF。解決:fgets($h) 讀第一行檢測並移除 BOM,或 fseek($h, 3) 跳過。
  • Excel 打開 CSV 科學計數法/日期誤判:在欄位值前加 \t 或 ='value' 強制文本格式,或改用 Excel 專用 XML/HTML 格式匯出。
  • fgetcsv 遇到多行欄位(含換行符):正常 CSV 以雙引號包裹多行,fgetcsv 原生支援,無需特殊處理。

總結

掌握 fgetcsv/fputcsv + iconv 編碼轉換 + 批量 INSERT/流式輸出 + 正確 HTTP 頭,即可構建健壯的 CSV 導入導出功能。大數據場景請優先考慮 MySQL 原生 LOAD DATA/INTO OUTFILE 或非同步佇列架構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved