この記事はtomita@atuwebがお届けします。
CSVをアップロードしてDBに登録するシステム実装を行います。

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

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

ファイルアップロード

HTML

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

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

CSRFについて

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

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

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

PHP

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

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

$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します。

{
    "require": {
        "goodby/csv": "*"
    }
}

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

use Goodby\CSV\Import\Standard\Lexer;
use Goodby\CSV\Import\Standard\Interpreter;
use Goodby\CSV\Import\Standard\LexerConfig;

$config = new LexerConfig();
$config->setDelimiter(",");

$interpreter = new Interpreter();
$interpreter->addObserver(function(array $columns) {
    // CSVファイルを1行ずつ処理
    DB::table('users')->insert(
        ['name' => $columns[0], 'email' => $columns[1] ]
    );
});

$lexer = new Lexer($config);
$lexer->parse('csv.csv', $interpreter);

$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を編集します。

設定例

memory_limit=128M
post_max_size=64M
upload_max_filesize=50M

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


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

post_max_size=10M
upload_max_filesize=8M

2015年12月17日 タイトルを「ちょっとだけ」変更

スポンサーリンク
ad_336
ad_336
  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存