CSVをアップロードしてDBに登録するシステム実装を行います。

利用バージョンはこちら。

  • PHP 5.4
  • Laravel 5.0.0
  • Bladeテンプレート

ファイルアップロード

HTML

Formタグに、いつものenctype="multipart/form-data"属性を追加します。

1<form method="POST" action="{{ url('/edit') }}" enctype="multipart/form-data">
2  <input type="hidden" name="_token" value="{{ csrf_token() }}">
3  <input type="file" name="data_file"><br>
4  <input type="submit" value="upload">
5</form>

CSRFについて

POSTで通信すると_自動でtokenのチェックが行われる_ため、hiddenでCSRFトークンを渡してやる必要があります。
トークンが得られなかった場合TokenMismatchExceptionがスローされます。

CSRFチェックを無効化することもできるようですね。

Laravel5.0 CSRFチェックを無効化
http://qiita.com/rana_kualu/items/3f9d0d6b9a363fd2108e

PHP

コントローラーでファイルを受け取ります。

1$file     = \Input::file('data_file');
2$fileName = $file->getClientOriginalName(). '_'. time();
3$move     = $file->move(storage_path(). '/upload', $fileName);
4

$file->move('/', fileName)のようにするとpublicディレクトリ以下にファイルが生成されてしまいましたので、Storageディレクトリ以下を指定しました。

storage_path()などの便利な関数は次のドキュメントをご覧ください。

Laravel 5.0.0 ヘルパー関数
http://readouble.com/laravel/5/0/0/ja/helpers.html

CSVのパース

CSVの処理にはgoodby/csvを利用しました。

goodby/csv
https://github.com/goodby/csv

composer.jsonに以下requireを追加し、composer upatecomposer dump-autoloadします。

1{
2    "require": {
3        "goodby/csv": "*"
4    }
5}

利用方法はこんな感じです。

 1use Goodby\CSV\Import\Standard\Lexer;
 2use Goodby\CSV\Import\Standard\Interpreter;
 3use Goodby\CSV\Import\Standard\LexerConfig;
 4
 5$config = new LexerConfig();
 6$config->setDelimiter(",");
 7
 8$interpreter = new Interpreter();
 9$interpreter->addObserver(function(array $columns) {
10    // CSVファイルを1行ずつ処理
11    DB::table('users')->insert(
12        ['name' => $columns[0], 'email' => $columns[1] ]
13    );
14});
15
16$lexer = new Lexer($config);
17$lexer->parse('csv.csv', $interpreter);
18

$lexer->parse()を実行すると、addObserver()で定義したクロージャ処理が開始します。
ほぼ、goodby/csvドキュメントサンプルのままでゴメンナサイ。

ほかにも有名なパーサがあるようですが、goodby/csvは1行ずつ処理できるため、メモリが気にならないのがGoodでした。

1行ずつ処理で速度面が気になる場合は、useで配列を外から参照で渡してバルクインサートするのが良いでしょう。

つまづいた点

検証で_50Mbほどと、無駄に大きいCSVファイル_を生成してアップロードできるか検証したとところ、なぜかTokenMismatchExceptionが発生してしまうことがありました。

アップロードファイルサイズを小さくし、5Mbとしたところ例外は発生しません。
例外がスローされる閾値がどこか検証します。

post_max_size

今回post_max_sizeが_デフォルト8M_であったため、CSRFトークンが巨大なファイルデータに押し出されてしまったようでした。

upload_max_filesizeはデフォルト2Mでしたが、この上限があってもファイルはいったんPOSTでファイル自体は送ってしまうようですね。

この設定はコードからini_set()できないため、php.iniを編集します。

設定例

1memory_limit=128M
2post_max_size=64M
3upload_max_filesize=50M

ビルドインサーバを再起動すると、50Mbの巨大なファイルがアップロードでいるようになりました。
こういった設定は触る頻度が低いため、すぐにわからないのが悔しいです。


さすがに50MbもあるCSVファイルは使いませんので、最終的にはまともな値に戻しました。

1post_max_size=10M
2upload_max_filesize=8M

PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応

竹澤 有貴,栗生 和明,新原 雅司,大村 創太郎
出版社:ソシム  発売日:2018-09-26

Amazonで詳細を見る

PHPフレームワーク Laravel入門

掌田津耶乃
出版社:秀和システム  発売日:2017-09-16

Amazonで詳細を見る

初めてのPHP

David Sklar
出版社:オライリージャパン  発売日:2017-03-18

Amazonで詳細を見る