Laravel 11 将大型 CSV 文件导入数据库示例

作者: 温新

图书: 【Laravel 11 实战】

阅读: 151

时间: 2025-02-11 00:49:45

本篇文章演示,如何使用 Laravel 11 导入大型 csv 格式的数据。

有时候,我们需要将大型的 CSV 文件,例如 1 GB、2 GB、4 GB 等,导入到数据库中。大文件可能会导致 Laravel 中的超时或其他问题,但我有一个完美的解决方案来将大型 CSV 文件导入到数据库中。我们将使用 Laravel 的LazyCollection、DB 类、以及 PHP 内置的 fopen() 和 fgetcsv() 函数来读取 CSV 文件,并将数据存储到数据库。

使用这种方法的主要优点是能够高效地处理非常大的文件,而不会一次性将所有数据加载到内存中,从而避免了内存溢出或脚本执行时间过长的问题。LazyCollection允许我们以惰性求值的方式遍历集合,即只在需要时才处理数据,这非常适合处理大数据集。结合fopen()fgetcsv(),我们可以逐行读取CSV文件,确保即使是非常大的文件也能被平稳处理。

下面是一个简化的流程说明:

  1. 使用fopen()打开CSV文件进行读取。
  2. 使用fgetcsv()逐行读取CSV文件的内容。
  3. 使用LazyCollection创建一个惰性集合,使得数据可以在不占用大量内存的情况下被迭代处理。
  4. 跳过CSV文件的标题行(如果有的话)。
  5. 将每一批数据(比如每100条记录)映射成适合插入数据库的格式。
  6. 使用DB::table()->insert()方法批量插入数据到数据库中,提高效率并减少数据库交互次数。

通过这种方式,即使是处理非常大的CSV文件,也能够有效地将其内容导入到数据库中,同时保持良好的性能和资源管理。

第一步:创建项目
$ laravel new democsv
第二步:创建迁移文件
$ php artisan make:migration create_products_table

编写迁移文件

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('amount');
            $table->text('description');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('products');
    }
};

执行迁移文件

$ php artisan migrate
第三步:创建数据填充文件

在这里,我们将创建 ProductSeeder 类并编写导入大型 csv 文件的代码。

确保您创建的文件products.csv “name”、“amount” 和 “description” 列。将该文件放在公共文件夹中。

现在,让我们使用以下命令创建 seeder 类:

$ php artisan make:seeder ProductSeeder
第四步:编写填充文件

database/seeders/ProductSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\LazyCollection;

class ProductSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // 禁用查询日志记录,以提高性能并减少内存使用
        DB::disableQueryLog();
        DB::table('products')->truncate();

        // 使用 LazyCollection 创建一个惰性集合,以便高效处理大型 CSV 文件
        LazyCollection::make(function () {
            // 打开位于 public 目录下的 'products.csv' 文件进行读取
            $handle = fopen(public_path('products.csv'), 'r');
            // 循环遍历每一行 CSV 数据
            while (($line = fgetcsv($handle, 4096)) !== false) {
                // 将一行的数据转换为字符串,并用逗号连接起来
                // 形式:手机, 10, 好手机
                $dataString = implode(", ", $line);
                // 将字符串再拆分成数组,这一步可能是多余的,因为 $line 已经是数组了
                // 此外,由于原始 CSV 分隔符已被移除,这里可能会导致数据不正确
                $row = explode(',', $dataString);
                // 每次迭代生成一个元素,即一行数据
                yield $row;
            }
            fclose($handle);
        })
            ->skip(1) // 跳过第一行(通常包含表头),只处理实际的数据行
            ->chunk(100) // 将数据分割成每块 100 条记录的分片,以批量插入数据库,提高效率
            ->each(function (LazyCollection $chunk) { // 对每个分片执行操作
                // 映射每一行数据到一个关联数组中,准备插入数据库
                $records = $chunk->map(function ($row) {
                    return [
                        'name' => $row[0],
                        'amount' => $row[1],
                        'description' => $row[2],
                    ];
                })->toArray();

                DB::table('products')->insert($records);
            });
    }
}
第五步:执行填充文件
$ php artisan db:seed --class=ProductSeeder
第六步:关于 csv 文件

我们需要自己创建一个 products.csv 文件并放到 public 目录下。其内容格式如下:

name amount description
手机 10 自己的手机
请登录后再评论