如何使用CodeIgniter导入含动态列的CSV文件?
CodeIgniter 实现动态列CSV成绩导入方案
场景说明
待导入CSV固定包含三列:user_id、Course_id、Marks,数据库中学生成绩表以user_id为固定维度,每个Course_id对应独立的动态列存储对应课程分数。
实现步骤
1. 前置配置
- 数据库成绩表设置固定字段:
id(主键自增)、user_id(用户ID,加索引),所有课程动态列统一用course_课程ID命名(比如课程ID为201则列名为course_201),字段类型设为FLOAT存储分数,避免纯数字列名触发SQL语法错误。 - 项目中提前加载CodeIgniter自带的数据库类、文件上传类,无需额外安装第三方CSV解析依赖。
- 在项目根目录创建
./uploads/temp/临时目录,设置可写权限,用于存放上传的临时CSV文件。
2. 控制器上传入口
在对应业务控制器中添加上传方法,先做文件格式校验:
// 成绩导入控制器方法 public function import_marks() { $config['upload_path'] = './uploads/temp/'; $config['allowed_types'] = 'csv'; $config['max_size'] = 2048; // 单文件最大2M,可根据实际需求调整 $this->load->library('upload', $config); if (!$this->upload->do_upload('csv_file')) { exit($this->upload->display_errors()); } $fileInfo = $this->upload->data(); $res = $this->process_csv_data($fileInfo['full_path']); // 处理完成删除临时文件 unlink($fileInfo['full_path']); exit("导入完成:成功{$res['success']}条,失败{$res['fail']}条"); }
3. CSV解析与动态导入逻辑
编写私有方法处理CSV读取、动态列检测、数据写入逻辑:
private function process_csv_data($filePath) { $handle = fopen($filePath, 'r'); // 读取第一行表头做格式校验 $header = fgetcsv($handle); array_walk($header, function(&$v){$v = trim(strtolower($v));}); if ($header != ['user_id', 'course_id', 'marks']) { exit('CSV格式错误,请确认表头顺序为user_id,Course_id,Marks'); } // 获取当前表已存在的所有课程列 $allFields = $this->db->list_fields('student_marks'); $existCourseCols = []; foreach ($allFields as $field) { // PHP8以下版本替换为 substr($field,0,7) === 'course_' if (str_starts_with($field, 'course_')) { $existCourseCols[] = $field; } } $success = 0; $fail = 0; // 逐行读取CSV数据 while (($row = fgetcsv($handle)) !== false) { $userId = intval(trim($row[0])); $courseId = intval(trim($row[1])); $marks = floatval(trim($row[2])); // 数据格式校验跳过非法行 if ($userId <=0 || $courseId <=0 || $marks < 0 || $marks > 100) { $fail++; continue; } $currentCol = 'course_'.$courseId; // 如果当前课程列不存在,自动给表新增列 if (!in_array($currentCol, $existCourseCols)) { $alterSql = "ALTER TABLE `student_marks` ADD COLUMN `$currentCol` FLOAT DEFAULT NULL COMMENT '课程{$courseId}成绩'"; $this->db->query($alterSql); $existCourseCols[] = $currentCol; } // 检查用户是否已有成绩记录 $userRecord = $this->db->get_where('student_marks', ['user_id' => $userId])->row_array(); if ($userRecord) { // 存在记录则更新对应课程分数 $this->db->where('user_id', $userId)->update('student_marks', [$currentCol => $marks]); } else { // 不存在则新建记录 $this->db->insert('student_marks', ['user_id' => $userId, $currentCol => $marks]); } $success++; } fclose($handle); return ['success' => $success, 'fail' => $fail]; }
4. 优化注意事项
- 编码兼容:Windows导出的CSV默认是GBK编码,读取行内容时可以用
mb_convert_encoding($row, 'UTF-8', 'GBK')转码,避免乱码。 - 大文件处理:单CSV超过1万行时,建议在方法开头加
set_time_limit(0)取消PHP执行时间限制,或者用前端分片上传方式导入,避免请求超时。 - 并发控制:多人同时导入时建议加文件锁,避免重复执行ALTER TABLE创建列的操作引发SQL报错。
- 数据校验:导入前可以先批量查询CSV中所有user_id是否存在于用户表,提前过滤无效用户ID,减少脏数据。
内容的提问来源于stack exchange,提问作者Eliya A.N
相关产品推荐
相关产品推荐

