起こったこと
PHP(laravel)でCSVアップロード機能を作成していたところ起こった事象。
CSVファイルのn行目のヘッダーの内容を確認しデータをDBに保存するという機能を作成していたのだが、ヘッダーの1列目を認識してくれないエラーが発生。
しかし、該当のCSVファイルはExcelから「名前を付けて保存」すると発生せず、正常にアップロードされるため困り果てていた。
CSVファイルの権限によるものではないかと考え調査していたが、めぼしい調査結果は得られずにいた。
原因
WinMergeで、元のファイルと「名前を付けて保存」し直したファイルを比較したところ、原因が発覚。
原因は「空行」だった。
実はCSVファイルデータのn-1行目には空行が入っており、それがSplFileObject::SKIP_EMPTYによって読み飛ばされていたことで、n行目のデータではなくn+1行目をヘッダーとして認識してしまい、それによりヘッダーが認識されずエラーが発生していたのだ。
$file->setFlags(
\SplFileObject::READ_CSV | // CSVとして行を読み込み
\SplFileObject::READ_AHEAD | // 先読み/巻き戻しで読み込み
\SplFileObject::SKIP_EMPTY | // 空行を読み飛ばす
\SplFileObject::DROP_NEW_LINE // 行末の改行を読み飛ばす
);
$header = $file->fgetcsv();
for ($i = 1; $i < (n-1); $i++) {
$header = $file->fgetcsv();
}
ちなみに「名前を付けて保存」し直したファイルでは、空行部分に「,(カンマ)」が自動で入れられていたため、空行扱いされず正常にアップロードが完了してしまっていた。
解決策
ヘッダーの読み込みを1回減らしてあげることで解決した。
$file->setFlags(
\SplFileObject::READ_CSV | // CSVとして行を読み込み
\SplFileObject::READ_AHEAD | // 先読み/巻き戻しで読み込み
\SplFileObject::SKIP_EMPTY | // 空行を読み飛ばす
\SplFileObject::DROP_NEW_LINE // 行末の改行を読み飛ばす
);
$header = $file->fgetcsv();
for ($i = 0; $i < (n-2); $i++) {
$header = $file->fgetcsv();
}
終わりに
SplFileObjectの存在を完全に見落としており、解決に2週間費やしました…。
大体のエラーはファイルのせいじゃなく自分が書いたコードのせいだという自分への戒めです。