😸
Laravelでバイト数のバリデートができたファイルだけ読み込む方法のメモ
はじめに
CSVで外部連携を行う場合、読込側でCSVデータが不正になっていないかを事前にチェックする方法があります。
例えば、実データを対になる制御ファイルを持っておき、制御ファイルに実データのバイト数を保持しておく方法です。
これにより、CSVデータが作成途中ではないことが確認できる場合があります。
こういった方法を取る場合に、
- 制御ファイルが存在するのか
- 制御ファイルに記載のバイト数と実データのバイト数が一致するのか
といった場合分けをしつつ読み込みする処理が必要になります。
Laravelでこれが必要になったので、手を動かしながら調べてみました。
やってみたこと
Laravelプロジェクトを作成
$ composer create-project laravel/laravel load-file-csv-with-ctl
動作確認用のファイルを用意
実データ(.csvで統一する)と制御ファイル(.ctlで統一する)をペアで作成し、それを以下のパターンで用意します。
- 正常
- .ctlのバイト数がおかしい
- .ctlが無い
作成後の状態はこんな感じです
$ cd storage/app/public/
$ head *
==> output_20210703072428.csv <==
1,apple,100
2,banana,150
3,cherry,200
==> output_20210703072428.ctl <==
38
==> output_20210703073006.csv <==
3,apple,100
2,banana,150
1,cherry,200
==> output_20210703073006.ctl <==
10
==> output_20210703073017.csv <==
3,apple,100
1,banana,150
2,cherry,200
処理を記述
routes/web.php
に処理をすべて書いてしまいます。
routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\File;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Route::get('/list', function () {
$path = storage_path( 'app/public' );
$fileNames = array();
$files = File::files( $path );
foreach( $files as $file ){
$fileNames[] = pathinfo( $file->getFilename(), PATHINFO_FILENAME );
}
$set = array_unique( $fileNames );
foreach( $set as $elm ){
$ctlContent = [];
$csvContent = [];
$target = $path . '/' . $elm;
// See: https://www.php.net/manual/ja/function.file-exists.php
if ( file_exists( $target . '.ctl' ) ){
$ctlContent = file_get_contents( $target . '.ctl' ) ?? '';
} else {
echo( $elm . '.ctl' . 'は存在しませんでした。');
}
if ( file_exists( $target . '.csv' ) ){
$csvContent = new \SplFileObject( $target . '.csv' );
$csvContent->setFlags(
\SplFileObject::READ_CSV | // CSV 列として行を読み込む
\SplFileObject::READ_AHEAD | // 先読み/巻き戻しで読み出す
\SplFileObject::SKIP_EMPTY | // 空行は読み飛ばす
\SplFileObject::DROP_NEW_LINE // 行末の改行を読み飛ばす
);
} else {
echo( $elm . '.csv' . 'は存在しませんでした。' . "\n");
}
if ( intval( $ctlContent ) !== $csvContent->getSize() ) {
echo( $elm . '.csv' . 'のサイズが.ctlファイルの内容と一致しません' . "\n");
} else {
echo( $csvContent . "\n");
}
}
});
補足
// See: https://www.php.net/manual/ja/function.file-exists.php if ( file_exists( $target . '.ctl' ) ){ $ctlContent = file_get_contents( $target . '.ctl' ) ?? ''; } else { echo( $elm . '.ctl' . 'は存在しませんでした。'); }
file_existsを使って、 file_exists( <フルパス> )
でファイルの存在確認をすることができます。
if ( intval( $ctlContent ) !== $csvContent->getSize() ) { echo( $elm . '.csv' . 'のサイズが.ctlファイルの内容と一致しません' . "\n"); } else {
.ctlから読み込んだ内容をintvalで数値に変換しないと、ここでは文字で評価されてしまいます。なので変換しています。
動作確認
- ローカルサーバを起動します
$ php artisan serve
- 正しい内容だけ取得できることを確認
$ curl http://localhost:8000/list
1,apple,100
output_20210703073006.csvのサイズが.ctlファイルの内容と一致しません
output_20210703073017.ctlは存在しませんでした。output_20210703073017.csvのサイズが.ctlファイルの内容と一致しません
1, apple, 100 が出力され、他は失敗することを確認できました。
Discussion