LaravelのMiddlewareとCookieでダウンロード2重クリック防止

こんにちは。山中です。今回は、簡単に出来そうなのに若干手こずったファイルダウンロード2重クリック防止について書きます。もっと詳しく書くと、ダウンロードの処理をサーバ側で行なっている間はボタンをdisabled等にして2回以上ダウンロードのリクエストが連続して実行されないようにするための処理です。流れとしては、

  • ファイルダウンロードボタンを押す
  • JSにてファイルダウンロードボタンのdisabled処理を行う
  • 毎秒JSにてダウンロード完了後にセットされるCookieを監視する
  • サーバ側のダウンロード処理が完了したらサーバ側でCookieをセットする
  • JSでCookie監視の処理でダウンロード完了を検出し、disabledを解除する

今回はPHPのフレームワークLaravelのMiddlewareを使って実装します。簡単に言えばリクエストを受けて処理を行う前、もしくは処理が完了した後に実行する処理を簡単に書くことができる機能です。
詳細は、公式マニュアル参考にしてください。
https://laravel.com/docs/5.6/middlewareまたは、https://readouble.com/laravel/5.6/ja/middleware.html

クライアントサイドのファイルダウンロード2重読み込み防止コード

<a href="{{ route('csv.download') }}" class="csv_download">CSVダウンロード</a>
<script>
    //Cookieのdownloadedの値を初期化(downloadedの名称はdownload_flagとかでもなんでもいいです)
    //pathのセットのところで
    $.removeCookie("downloaded", { path:  "/" });
    console.log($.cookie("downloaded"));
    // aタグで行う場合は、clickイベントで同様のことを行う
    $('.csv_download').on('click', function() {
        if(($('.csv_download').attr('disabled') == 'disabled')) {
            return false;
        } else {
            $('.csv_download').attr("disabled", true);
        }
        //毎秒Cookieのdownloadedにセットされる値をチェック
        $interval = setInterval(function () {
            if ($.cookie("downloaded")) {
                $('.csv_download').attr("disabled", false);
                clearInterval($interval);
                console.log($.cookie("downloaded"));
                $.removeCookie("downloaded", { path:  "/" });
            } else {
                console.log($.cookie("downloaded"));
            }
        }, 1000);
    });
</script>

removeCookieでCookieを初期化して、ダウンロードボタンをクリックした時からsetIntervalで毎秒Cookieの値をチェックし、サーバ側のダウンロード処理が完了し、Cookieがセットされた段階でdisabled解除とCookieの初期化、clearIntervalを行う。

LaravelのMiddlewareを使用したサーバサイド側のファイルダウンロード2重読み込み防止コード

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Cookie;

class AfterMiddleware
{
    //CSVダウンロード完了後にクッキーをtrue
    public function handle($request, Closure $next)
    {
        $response = $next($request);
        header('Set-Cookie: downloaded=true; path=/');
        return $response;
    }
}

上記のMiddlewareを呼び出すようにすれば、Controllerのactionが実行完了した時にCookieをセットできるようになります。※補足:Laravelのヘルパー等でクッキーを使用するとデフォルトで暗号化されるのでJSからは基本的に閲覧することができないです。例外を追加すれば可能なのですが、今回はphpのheader関数でCookieをセットするようにしました。また、path=/の関係だと思うのですが、Laravelのヘルパーを使用するとなぜかJSで取得ができなかったり動作がわからなかった、Laravelのヘルパーを使用するとなぜかWindowsで動かないといった謎の現象が起きたので、phpのheader関数を使ってます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

ABOUTこの記事をかいた人

山中 雅彦

大分でWebシステム受託開発会社のバックエンドエンジニアとして働いてます。 Laravel5でのシステム開発を中心に仕事してます。 高校卒業後に鉄鋼会社で電気整備の仕事をする傍ら、HTML、CSS、JS、PHP、MySQL、サーバ関連技術を独学で勉強。 2017年9月にWebシステム開発会社に入社し、システム開発をしています。 Web技術全般が好きなPHPerです。