isaacdew / load-data
使用 Laravel 的 LOAD DATA INFILE 在 MySQL 中加载大型 CSV 文件的流畅语法。
Requires (Dev)
- laravel/pint: ^1.14
- orchestra/testbench: ^8.21
README
MySQL 和 MariaDB 内置了 LOAD DATA INFILE 语句,该语句允许将大型数据集从 CSV 或类似文件快速加载到表中。此包提供了在 Laravel 中构建和执行 LOAD DATA INFILE
语句的 API。
安装
使用 composer 安装此包
composer require isaacdew/load-data
示例
基本示例
use Isaacdew\LoadData\LoadData; LoadData::from(storage_path('path/to/file.csv')) ->to('tablename') ->fieldsTerminatedBy(',') ->fieldsEnclosedBy('"', optionally: true) ->load();
忽略 CSV 头部
use Isaacdew\LoadData\LoadData; LoadData::from(storage_path('path/to/file.csv')) ->to('tablename') ->ignoreHeader() ->load();
定义列
use Isaacdew\LoadData\LoadData; LoadData::from(storage_path('path/to/file.csv')) ->to('tablename') ->fieldsTerminatedBy(',') ->fieldsEnclosedBy('"', optionally: true) ->columns([ 'column_one', 'column_two', 'column_three' ]) ->load();
使用 CSV 的头部定义您的列
use Isaacdew\LoadData\LoadData; LoadData::from(storage_path('path/to/file.csv')) ->to('tablename') ->fieldsTerminatedBy(',') ->fieldsEnclosedBy('"', optionally: true) ->useFileHeaderForColumns() ->load();
默认情况下,CSV 头部会被转换为蛇形命名,因为列名需要与数据库表中的列名匹配。如果您需要进行任何自定义修改,可以传递一个回调给 useFileHeaderForColumns
方法。
use Isaacdew\LoadData\LoadData; LoadData::from(storage_path('path/to/file.csv')) ->to('tablename') ->fieldsTerminatedBy(',') ->fieldsEnclosedBy('"', optionally: true) // Remove parenthesis from column names ->useFileHeaderForColumns(fn($column) => preg_replace('/(\(.*)$/', '', $column)) ->load();
仅从 CSV 加载特定列
use Isaacdew\LoadData\LoadData; LoadData::from(storage_path('path/to/file.csv')) ->to('tablename') ->fieldsTerminatedBy(',') ->fieldsEnclosedBy('"', optionally: true) ->useFileHeaderForColumns() ->onlyColumns([ 'column_one', 'column_two' ]) ->load();
在加载前截断表
use Isaacdew\LoadData\LoadData; LoadData::from(storage_path('path/to/file.csv')) ->to('tablename') ->truncateBeforeLoad() ->load();
使用 SET 语句
要使用此功能,您必须首先使用 columns
方法或使用文件头部通过 useFileHeaderForColumns
方法定义列。然后您可以使用 MySQL 表达式修改 CSV 中的值。一个好的用例是日期列,CSV 中没有使用 MySQL 便于格式的日期。请注意,您必须使用 @
前缀来在表达式中使用列名。
use Isaacdew\LoadData\LoadData; LoadData::from(storage_path('path/to/file.csv')) ->to('tablename') ->fieldsTerminatedBy(',') ->fieldsEnclosedBy('"', optionally: true) ->columns([ 'column_one', 'column_two', 'column_three', 'date_column' ]) ->setColumn('date_column', "STR_TO_DATE(@date_column, '%c/%d/%Y')") // Convert MM/DD/YYYY to a MySQL date ->load();
专用数据库服务器
如果您的 Laravel 应用程序不在与数据库相同的服务器上,您必须确保数据库服务器和 PDO 中启用了 LOAD DATA LOCAL INFILE 语句。此包将在 DB_HOST
设置为 127.0.0.1
或 localhost
之外时自动使用 LOCAL
关键字。
要启用 PDO 中的 LOAD DATA LOCAL INFILE
,请转到您的 config/database.php
文件,并在 mysql
连接的选项数组中添加 PDO::MYSQL_ATTR_LOCAL_INFILE => true
,如下所示
return [ 'connections' => [ //... 'mysql' => [ 'driver' => 'mysql', 'url' => env('DATABASE_URL'), 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'unix_socket' => env('DB_SOCKET', ''), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'prefix_indexes' => true, 'strict' => true, 'engine' => null, 'options' => extension_loaded('pdo_mysql') ? array_filter([ PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), PDO::MYSQL_ATTR_LOCAL_INFILE => true // Add this line! ]) : [], ], //... ] ];
关于安全性的说明
对于 LOAD DATA INFILE
语句,不支持预处理语句。在这种情况下,不要使用用户输入来构建 LOAD DATA INFILE
语句。我已经采取了预防措施使用 PDO::quote()
来转义文件名,但我仍然建议不要在此语句中使用用户提供的文件名。