使用 Laravel simple-excel 处理大型 CSV 文件
在 Laravel 中,可以使用队列处理大型CSV文件的上传,以避免因此引起的超时或内存使用问题,尤其是当您有大量数据需要分析、报告或移动时。
安装 spatie/simple-excel 拓展包
bash
composer require spatie/simple-excel
使用 SimpleExcelReader
加载 CVS。默认情况下,它会返回一个 Illuminate\Support\LazyCollection
将其视为处理数据的更周到的方式,而不会耗尽服务器的内存。这意味着可以一点一点地处理文件,使用程序保持轻松运行。
$rows
是Illuminate\Support\LazyCollection
的实例。
创建 Jobs
使用下面的命令创建一个任务用于导入 CSV 的操作。
bash
php artisan make:job ImportCsv
任务类的大致代码逻辑如下:
php
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Spatie\SimpleExcel\SimpleExcelReader;
class ImportCsv implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*/
public function __construct()
{
//
}
/**
* Execute the job.
*/
public function handle(): void
{
SimpleExcelReader::create(storage_path('app/public/products.csv'))
->useDelimiter(',')
->useHeaders(['ID', 'title', 'description'])
->getRows()
->chunk(5000)
->each(
// Here we have a chunk of 5000 products
);
}
}
- 对 CSV 进行分块:将把该文件分成可管理的部分,以便可以使用
LazyCollection
。 - 任务调度:对于每个块,将发送一个任务。这样就可以批量处理,这在服务器上会更容易。
- 写入数据库:每个块将被插入到数据库中,既简单又好。
处理分块Job
使用下面的命令创建 ImportProductChunk
任务。
bash
php artisan make:job ImportProductChunk
它用于批量将数据写入到数据库。
php
<?php
namespace App\Jobs;
use App\Models\Product;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str;
class ImportProductChunk implements ShouldBeUnique, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $uniqueFor = 3600;
/**
* Create a new job instance.
*/
public function __construct(
public $chunk
) {
//
}
/**
* Execute the job.
*/
public function handle(): void
{
$this->chunk->each(function (array $row) {
Model::withoutTimestamps(fn () => Product::updateOrCreate([
'product_id' => $row['ID'],
'title' => $row['title'],
'description' => $row['description'],
]));
});
}
public function uniqueId(): string
{
return Str::uuid()->toString();
}
}
创建好之后,可以在 ImportCsv 任务类中调用它。
php
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Spatie\SimpleExcel\SimpleExcelReader;
class ImportCsv implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*/
public function __construct()
{
//
}
/**
* Execute the job.
*/
public function handle(): void
{
SimpleExcelReader::create(storage_path('app/public/products.csv'))
->useDelimiter(',')
->useHeaders(['ID', 'title', 'description'])
->getRows()
->chunk(5000)
->each(
fn ($chunk) => ImportProductChunk::dispatch($chunk)
);
}
}