
本文详解如何使用 php 的 LOAD DATA LOCAL INFILE 安全、可靠地将本地上传的 csv 文件批量导入 mysql 数据库表,涵盖路径处理、SQL 语法修正、权限配置及常见错误规避。
本文详解如何使用 php 的 `load data local infile` 安全、可靠地将本地上传的 csv 文件批量导入 mysql 数据库表,涵盖路径处理、sql 语法修正、权限配置及常见错误规避。
在 Web 应用中,通过表单上传 CSV 并批量写入数据库是高频需求。但直接使用 LOAD DATA LOCAL INFILE 时极易因路径误用、引号缺失或权限限制而报错(如 SQLSTATE[42000]: Syntax Error… near ‘student.csv INTO table…’)。根本原因在于:LOAD DATA LOCAL INFILE 要求文件路径指向数据库服务器上的可访问位置,而非客户端原始文件名;且文件名必须用单引号包裹。
以下为经过生产验证的完整实现方案:
✅ 正确步骤与关键修复点
-
获取并保存临时文件到服务端可读路径
浏览器上传的文件仅暂存于 $_FILES[“myfile”][“tmp_name”],需主动移动至脚本同级(或其他指定)目录:$uploadedFile = $_FILES["myfile"]["name"]; $targetPath = "./" . basename($uploadedFile); if (!move_uploaded_file($_FILES["myfile"]["tmp_name"], $targetPath)) { die("文件上传失败:请检查目录是否具有写入权限(chmod 755 或 777)"); } -
构造合法 SQL —— 必须使用单引号包裹文件路径
原代码中 $uploadedFile 未加引号,导致 SQL 解析为 LOAD DATA LOCAL INFILE student.csv …(非法标识符),正确写法为:$sql = "LOAD DATA LOCAL INFILE '" . $targetPath . "' INTO TABLE " . $studentTable . " FIELDS TERMINATED BY ',' LINES TERMINATED BY 'n' IGNORE 1 LINES";⚠️ 注意:LINES TERMINATED BY ‘n’ 适用于 unix/linux 换行;若 CSV 来自 windows,应改为 ‘rn’;Mac 可能为 ‘r’。建议统一用 file() + fgetcsv() 预检首行换行符。
-
pdo 连接必须启用 LOCAL_INFILE 支持
除连接选项外,还需确保 MySQL 服务端允许该操作(部分托管环境默认禁用):$conn = new PDO( "mysql:host=$servername;dbname=$dbname", $username, $password, [ PDO::MYSQL_ATTR_LOCAL_INFILE => true, // 启用 LOCAL INFILE PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ] );
? 安全与稳定性增强建议
-
路径白名单校验:防止目录遍历攻击
立即学习“PHP免费学习笔记(深入)”;
$safeName = preg_replace('/[^a-zA-Z0-9_.-]/', '', $uploadedFile); // 仅保留安全字符 $targetPath = "./uploads/" . $safeName; // 推荐专用 uploads/ 子目录 -
CSV 格式预校验(可选但推荐)
在执行 LOAD DATA 前,用 fgetcsv() 读取前几行验证字段数、编码(UTF-8 bom)、分隔符一致性:$handle = fopen($targetPath, 'r'); $firstRow = fgetcsv($handle, 0, ','); fclose($handle); if (!$firstRow || count($firstRow) !== 5) { // 假设目标表有 5 列 throw new Exception("CSV 列数不匹配,请检查格式"); } -
MySQL 服务端配置确认
执行 SHOW VARIABLES LIKE ‘local_infile’;,确保值为 ON;若为 OFF,需在 MySQL 配置文件中添加:[mysqld] local_infile=ON [client] local_infile=ON并重启服务(部分云数据库需在控制台开启「本地文件导入」开关)。
✅ 最终可运行示例(精简版)
<?php $servername = "localhost"; $username = "root"; $password = ""; $dbname = "onlinepaydb"; $studentTable = "studentregtable"; if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_FILES["myfile"])) { $originalName = $_FILES["myfile"]["name"]; $safeName = preg_replace('/[^a-zA-Z0-9_.-]/', '', $originalName); $targetPath = "./uploads/" . $safeName; // 创建 uploads 目录(若不存在) if (!is_dir("./uploads")) mkdir("./uploads", 0755, true); if (!move_uploaded_file($_FILES["myfile"]["tmp_name"], $targetPath)) { die("文件保存失败,请检查 ./uploads 目录权限"); } try { $conn = new PDO( "mysql:host=$servername;dbname=$dbname", $username, $password, [PDO::MYSQL_ATTR_LOCAL_INFILE => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION] ); $sql = "LOAD DATA LOCAL INFILE '" . $targetPath . "' INTO TABLE " . $studentTable . " FIELDS TERMINATED BY ',' LINES TERMINATED BY 'n' IGNORE 1 LINES"; $conn->exec($sql); echo "<p style='color:green'>✅ 成功导入:" . $conn->lastInsertId() . " 行数据</p>"; } catch (PDOException $e) { echo "<p style='color:red'>❌ 导入失败:" . htmlspecialchars($e->getMessage()) . "</p>"; } } ?> <!DOCTYPE html> <html> <head><meta charset="UTF-8"></head> <body> <form method="post" enctype="multipart/form-data"> <label>选择 CSV 文件(首行为字段名):</label> <input type="file" name="myfile" accept=".csv" required><br><br> <button type="submit">开始导入</button> </form> </body> </html>
? 总结:LOAD DATA LOCAL INFILE 是高效批量导入方案,但成功依赖三要素——服务端文件路径真实存在 + SQL 中路径加单引号 + MySQL 服务端及 PDO 双重启用 local_infile。跳过任一环节均会导致语法错误或拒绝访问。生产环境务必加入文件校验与异常兜底,避免数据污染。