1. 环境准备:安装 PhpSpreadsheet 库。
  2. 控制器代码:编写处理上传和导入逻辑的核心代码。
  3. 视图代码:创建上传表单页面。
  4. 模型代码:将数据保存到数据库。
  5. 完整示例与总结:整合所有代码并补充注意事项。

第一步:环境准备

  1. ThinkPHP 项目:确保你已经有一个可运行的 ThinkPHP 项目。

    thinkphp导入excel教程
    (图片来源网络,侵删)
  2. 安装 PhpSpreadsheet:我们使用 Composer 来安装这个库,在你的项目根目录下(与 composer.json 同级)打开终端或命令行,运行以下命令:

    composer require phpoffice/phpspreadsheet

    安装完成后,vendor 目录下会出现 phpoffice/phpspreadsheet 文件夹,composer.json 会自动更新。


第二步:创建数据库和数据表

假设我们要导入的是用户信息,包含 name(姓名)、email(邮箱)和 age(年龄)。

  1. 创建一个 users 表:
    CREATE TABLE `users` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(50) NOT NULL COMMENT '姓名',
      `email` varchar(100) NOT NULL COMMENT '邮箱',
      `age` int(11) DEFAULT NULL COMMENT '年龄',
      `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      PRIMARY KEY (`id`),
      UNIQUE KEY `email` (`email`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

第三步:创建控制器

app/controller 目录下创建一个控制器,ExcelController.php

thinkphp导入excel教程
(图片来源网络,侵删)
<?php
namespace app\controller;
use app\BaseController;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Reader\IReadFilter;
use think\facade\Db;
use think\exception\ValidateException;
class ExcelController extends BaseController
{
    /**
     * 显示上传表单
     */
    public function index()
    {
        return view();
    }
    /**
     * 处理 Excel 文件上传和数据导入
     */
    public function import()
    {
        // 获取上传的文件
        $file = request()->file('excel');
        if (empty($file)) {
            return json(['code' => 0, 'msg' => '请选择要导入的Excel文件']);
        }
        try {
            // 验证文件
            validate(['excel' => 'fileExt:xls,xlsx'])
                ->check(['excel' => $file]);
            // 读取 Excel 文件
            $reader = IOFactory::createReaderForFile($file->getPathname());
            $reader->setReadDataOnly(true); // 只读取数据,不读取格式
            $spreadsheet = $reader->load($file->getPathname());
            $sheet = $spreadsheet->getActiveSheet();
            // 获取最高行数
            $highestRow = $sheet->getHighestRow();
            // 从第二行开始读取(假设第一行是标题)
            $successCount = 0;
            $errorRows = [];
            for ($row = 2; $row <= $highestRow; $row++) {
                // 获取单元格数据
                $name = $sheet->getCell('A' . $row)->getValue();
                $email = $sheet->getCell('B' . $row)->getValue();
                $age = $sheet->getCell('C' . $row)->getValue();
                // 数据验证
                if (empty($name) || empty($email)) {
                    $errorRows[] = "第 {$row} 行:姓名和邮箱不能为空";
                    continue;
                }
                if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
                    $errorRows[] = "第 {$row} 行:邮箱格式不正确";
                    continue;
                }
                // 使用 Db 类进行数据插入
                Db::name('users')->insert([
                    'name' => $name,
                    'email' => $email,
                    'age' => is_numeric($age) ? intval($age) : null,
                ]);
                $successCount++;
            }
            $message = "导入成功!共处理 {$highestRow - 1} 行,成功导入 {$successCount} 条。";
            if (!empty($errorRows)) {
                $message .= "\n以下行导入失败:\n" . implode("\n", $errorRows);
            }
            return json(['code' => 1, 'msg' => $message]);
        } catch (ValidateException $e) {
            // 验证失败
            return json(['code' => 0, 'msg' => $e->getError()]);
        } catch (\Exception $e) {
            // 其他异常
            return json(['code' => 0, 'msg' => '导入失败:' . $e->getMessage()]);
        }
    }
}

代码解析

  1. index():用于显示上传表单。
  2. import():核心处理方法。
    • request()->file('excel'):获取前端名为 excel 的文件。
    • validate(...):使用 ThinkPHP 的验证器,限制文件后缀为 xlsxlsx
    • IOFactory::createReaderForFile():根据文件类型自动创建合适的读取器。
    • load():加载 Excel 文件。
    • getActiveSheet():获取当前活动的工作表。
    • getHighestRow():获取工作表的最高行数,用于循环。
    • 循环读取:从第 2 行开始(跳过标题行),通过 getCell('A' . $row)->getValue() 获取 A、B、C 列的数据。
    • 数据验证:对读取的数据进行简单的业务逻辑验证(如非空、邮箱格式)。
    • Db::name('users')->insert():使用 ThinkPHP 的数据库门面,将数据插入到 users 表。
    • 异常处理:使用 try...catch 捕获可能发生的错误,如文件格式错误、数据验证失败、数据库操作失败等,并返回友好的提示。

第四步:创建视图文件

app/view 目录下创建一个与控制器同名的目录 excel,然后在里面创建 index.html 文件。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">ThinkPHP 导入 Excel 教程</title>
    <style>
        body { font-family: Arial, sans-serif; line-height: 1.6; margin: 20px; }
        .container { max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #ddd; border-radius: 5px; }
        h1, h2 { color: #333; }
        .upload-area { border: 2px dashed #ccc; padding: 20px; text-align: center; margin-bottom: 20px; }
        input[type="file"] { margin: 10px 0; }
        button { background-color: #4CAF50; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
        button:hover { background-color: #45a049; }
        .result { margin-top: 20px; padding: 10px; border-radius: 4px; }
        .success { background-color: #dff0d8; color: #3c763d; }
        .error { background-color: #f2dede; color: #a94442; }
    </style>
</head>
<body>
    <div class="container">
        <h1>用户信息导入</h1>
        <p>请下载模板,填写数据后上传,Excel 第一行为标题(姓名, 邮箱, 年龄),从第二行开始为数据。</p>
        <a href="/public/template.xlsx" download>点击下载 Excel 模板</a>
        <div class="upload-area">
            <form id="uploadForm" enctype="multipart/form-data">
                <input type="file" name="excel" id="excel" accept=".xls,.xlsx" required>
                <br>
                <button type="submit">导入数据</button>
            </form>
        </div>
        <div id="result" class="result" style="display: none;"></div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
    <script>
    $(function(){
        $('#uploadForm').on('submit', function(e){
            e.preventDefault(); // 阻止表单默认提交
            var formData = new FormData(this);
            var submitBtn = $(this).find('button');
            submitBtn.prop('disabled', true).text('导入中...');
            $.ajax({
                url: '{:url("excel/import")}',
                type: 'POST',
                data: formData,
                processData: false,
                contentType: false,
                success: function(res) {
                    $('#result').removeClass('success error');
                    if(res.code === 1) {
                        $('#result').addClass('success').html('<strong>成功:</strong>' + res.msg).show();
                    } else {
                        $('#result').addClass('error').html('<strong>失败:</strong>' + res.msg).show();
                    }
                },
                error: function() {
                    $('#result').addClass('error').html('<strong>错误:</strong>服务器请求失败,请稍后再试。').show();
                },
                complete: function() {
                    submitBtn.prop('disabled', false).text('导入数据');
                }
            });
        });
    });
    </script>
</body>
</html>

视图解析

  1. 表单<form> 标签设置了 enctype="multipart/form-data",这是文件上传所必需的。
  2. 文件输入<input type="file" name="excel">name 属性必须与控制器中 request()->file('excel') 的参数一致。
  3. 模板下载:提供一个 Excel 模板的下载链接,方便用户按格式填写,你需要手动创建一个 public/template.xlsx 文件,并填好标题行。
  4. AJAX 提交:使用 jQuery 的 AJAX 来异步提交表单,避免页面刷新,提升用户体验。
  5. 结果展示:通过一个 div 来显示导入成功或失败的提示信息。

第五步:配置路由(可选但推荐)

为了方便访问,可以在 route/app.php 中配置路由:

thinkphp导入excel教程
(图片来源网络,侵删)
use think\facade\Route;
// Excel 导入相关路由
Route::get('excel', 'ExcelController/index');
Route::post('excel/import', 'ExcelController/import');

配置后,访问 http://你的域名/excel 即可看到上传页面。


总结与注意事项

  1. 文件大小限制:PHP 默认的上传文件大小可能很小(如 2M),你需要在 php.ini 中修改 upload_max_filesizepost_max_size 的值。

    upload_max_filesize = 20M
    post_max_size = 20M

    修改后需要重启 PHP-FPM 或 Apache 服务。

  2. 内存占用:PhpSpreadsheet 在处理大型 Excel 文件时会占用较多内存,如果文件非常大,可以考虑:

    • 服务器增加内存。
    • 使用 setReadDataOnly(true) 减少内存消耗。
    • 对于超大数据,可能需要考虑更底层的处理方式或分批读取。
  3. 数据验证:本教程中的数据验证比较简单,在实际项目中,你可能需要更复杂的验证规则,例如检查手机号格式、身份证号、重复数据等。

  4. 事务处理:为了保证数据一致性,如果导入过程中某条数据出错,你可能希望回滚之前所有成功的导入,这时可以使用数据库事务。

    Db::startTrans();
    try {
        // ... 循环插入数据的代码 ...
        Db::commit();
        return json(['code' => 1, 'msg' => '导入成功']);
    } catch (\Exception $e) {
        Db::rollback();
        return json(['code' => 0, 'msg' => '导入失败:' . $e->getMessage()]);
    }
  5. 性能优化:对于上万条数据,循环逐条插入数据库性能会很差,可以优化为批量插入:

    $dataToInsert = [];
    for ($row = 2; $row <= $highestRow; $row++) {
        // ... 数据读取和验证 ...
        $dataToInsert[] = [
            'name' => $name,
            'email' => $email,
            'age' => $age,
        ];
    }
    // 分批插入,例如每 500 条一批
    array_chunk($dataToInsert, 500);
    foreach (array_chunk($dataToInsert, 500) as $chunk) {
        Db::name('users')->insertAll($chunk);
    }

至此,一个完整的 ThinkPHP 导入 Excel 功能就实现了,你可以根据这个基础框架进行扩展和优化。