JavaのWebアプリケーション開発フレームワークによる、Webサイト開発の顛末記です。

EclipseのMavenを使った、Spring-MVC、Thymeleaf、MyBatis 等のプログラミングテクニックを、
備忘録的に記録しています。実際に動くソースコードを多用して説明していますので、
これからEclipseや、Spring-MVCを始めたいと思っている人にとって、少しでも参考になれば幸いです。
Spring-MVCの散歩道 > 応用の森(jQuery/JavaScript 編) > dropzone.jsを使用した、Ajaxでのファイルアップロードサンプル

$(function(){

    /**
     * アップロードチェック用のファイルタイプと拡張子定義
     */
    FileTypes = {
        image: {
            description:'画像ファイル',
            matcher:[/^image¥//],
            extension: [/¥.jpeg$/i,/¥.jpg$/i, /¥.gif$/i, /¥.png$/i, /¥.svg$/i]
        },
        audio: {
            description:'音声ファイル',
            matcher:['audio/mp3'],
            extension: [/¥.mp3$/i]
        },
        video: {
            description:'動画ファイル',
            matcher:['video/quicktime', 'video/mp4'],
            extension: [/¥.mov$/i, /¥.mp4$/i]
        },
        excel: {
            description:'エクセルファイル',
            matcher:[
                //'application/vnd.ms-excel',
                //'application/vnd.ms-excel.sheet.macroEnabled.12',
                /^application¥/vnd.ms-excel/,
                'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' // *.xlsx
            ],
            extension: [/¥.xls$/i, /¥.xlsx$/i, /¥.xlsm$/i]
        },
        csv: {
            description: 'CSVファイル',
            //csv形式ファイルの場合、file.type == ""(空文字) で来ることがある
            //OS上のファイル⇔アプリケーション関連付けの問題
            matcher: ['', ,text/csv', 'application/vnd.ms-excel'],
            extension: [ /¥.csv$/i ]
        },
        xml: {
            description: 'XMLファイル',
            matcher: ['text/xml']
        },
        text: {
            description: 'テキストファイル',
            matcher: ['text/plain']
        },
        pdf: {
            description: 'PDFファイル',
            extension: [ /¥.pdf$/i ]
        },
        zip: {
            description: 'ZIPファイル',
            extension: [/¥.zip$/i ]
        }
    };
    /**************************************************/

    /**
     * Dropzone オブジェクト
     * opt.selector   :dropzone セレクタ
     * opt.upload     : アップロードボタンセレクタ
     * opt.maxfilesiz:アップロード可能ファイルタイプ
     * opt.maxfiles  :アップロード可能ファイルサイズ
     * opt.filetype   :添付ファイル最大数
     * ※注意)underscore.js を必要とします。
     */
    myDropzone = function(opt) {

        var _maxsize = opt.maxfilesize;
        var _maxFiles = opt.maxfiles;
        var _filetype = opt.filetype;
        var _upload_selector = opt.upload;  //アップロードボタン
        var _reason = "";

        Dropzone.autoDiscover = false;      //おまじない(これ指定しないと、いきなりinit定義の関数が実行された)

        //Dropzoneインスタンス生成
        uiDropzone =  new Dropzone(opt.selector, {
            //アップロード処理自体は、Dropzoneの機能ではなく
            //別のAjaxなんかを使って行うのでここはダミーでよい
            "url":"#",

            //ファイルドロップ時ハンドラの定義
            init: function() {
                this.on("addedfile", checkUploadStatusHandler),        //ファイル追加時
                this.on("removedfile", checkUploadStatusHandler),    //ファイル削除時
                this.on("maxfilesexceeded", checkReachHandler)       //ファイル数がmaxFilesの指定を超えてた時に呼び出される。
            },
            //acceptファイルを受け取ってからの処理を書き換える。
            accept: checkAccept,        //ファイルがドロップされたときの、ファイルタイプのチェックハンドラ
            autoDiscover:false,
            autoProcessQueue:false, //ドロップした時点で送信実行しない
            paramName: "file",      // The name that will be used to transfer the file
            uploadMultiple:"false", //falseでも複数大丈夫みたい
            maxFiles:_maxFiles,     //1度にアップロード出来るファイルの数
            maxFilesize: _maxsize,  //1つのファイルの最大サイズ( MB)
            addRemoveLinks: 'true',
            dictDefaultMessage: '<span class="icon icon-ui-file"></span>ファイルをここにドラッグアンドドロップ<small>またはクリックで場所を指定</small>',
            dictFallbackMessage: "ブラウザーがドラッグアンドドロップでのアップロードに対応していません",
            dictFileTooBig: "サイズが大きすぎます({{filesize}}MB)。上限は{{maxFilesize}}MBです",
            dictInvalidFileType: "この形式のファイルはアップロードできません",
            dictResponseError: "エラーが発生しました。コード: {{statusCode}}",
            dictCancelUpload: "アップロードをキャンセル",
            dictCancelUploadConfirmation: "アップロードをキャンセルしてよろしいですか",
            dictRemoveFile: "",
            dictMaxFilesExceeded: "アップロード可能なファイル数の上限に達しました。",
        });

        /**
         * ファイルタイプのチェックを行います
         * file : チェックするファイル情報
         */
        function checkFiletype(file){
            // 1) ファイル種別チェック
            //ファイルタイプのマッチングチェック
            // str : checkする文字列
            // matcher:checkパターン
            var isMatch = function(str, matcher){
                if(str.length==0) return true;
                //正規表現チェック
                var chk = _.isRegExp(matcher);
                if(chk){
                    return str.match(matcher);  //正規表現match
                }else{
                    return str == matcher;      //文字列match
                }
                return false;
            };

            var isOK = false;

            // 1-1) コンテントタイプチェック
            if (_.isArray(_filetype.matcher) && !_.isEmpty(_filetype.matcher)) {
                for (var i = 0; i < _filetype.matcher.length; i++) {
                    var matcher = _filetype.matcher[i];
                    var ret = isMatch(file.type, matcher);
                    if (ret) {
                        isOK = true;
                        break;
                    }
                }
                if(isOK === false){
                    _reason = _filetype.description + 'を選択してください。';
                    return false;
                }
            }

            // 1-2) ファイル拡張子チェック
            if (_.isArray(_filetype.extension) && !_.isEmpty(_filetype.extension)) {
                isOK = false;
                for (var i = 0; i < _filetype.extension.length; i++) {
                    var ext = _filetype.extension[i];
                    var ret = isMatch(file.name, ext);
                    if (ret) {
                        isOK = true;
                        break;
                    }
                }
                if(isOK === false){
                    _reason = _filetype.description + 'を選択してください。';
                    return false;
                }
            }

            if(isOK) {
                // 2) ファイルサイズチェック
                // サイズチェック(通常はcheckAcceptじゃなく、Dropzoneのデフォルトチェックが先に判定される)
                if(_maxsize > 0 && file.size > (_maxsize*1024*1024)){
                    // メッセージは Dropzone.options.fileupload.dictFileTooBig が表示される。
                    return false;           //Check NG
                }else if(file.size <= 0){
                    _reason = '空ファイルです。';
                    return false;           //Check NG
                }
                return true;            //Check OK
            }
        }

        /**
         * Dropzoneドロップファイルタイプのチェックハンドラ
         * file : ドロップされたファイル情報
         * done : 処理結果表示
         */
        function checkAccept(file, done){
            if (_filetype) {
                //ファイルタイプをチェックする
                var isOK = checkFiletype(file);

                if(isOK){       //添付ファイル正常時
                    //自分の親セレクタを見つけて、その子セレクタの吹き出し領域を非表示
                    $(file.previewElement).find('.dz-error-message').hide();
                    $(file.previewElement).find('.dz-progress').hide();	//progress barも隠蔽
                    done();
                }else{          //添付ファイル異常時
                    done(_reason);  //NGならメッセージを表示
                }
            }
        }

        /**
         * Dropzoneドロップファイル状態チェックイベントハンドラ
         * UI コントロール
         * ファイルタイプ・ファイルサイズ・ファイル添付数の状態をチェックして
         * 「アップロード」ボタンの表示/非表示を設定する
         */
        function checkUploadStatusHandler(){
            if(_upload_selector) {      //「アップロード」ボタンセレクタ指定あり
                var isOK = true;

                if (uiDropzone.files.length==0 || uiDropzone.files.length > _maxFiles) {
                    //添付ファイルがあり、最大添付数を超えたら「アップロード」ボタンを非表示
                    isOK = false;
                } else {
                    isOK = true;
                    for(i=0 ; i < uiDropzone.files.length ; i++) {
                        //添付されたファイルタイプ・サイズをチェックする
                        isOK = checkFiletype(uiDropzone.files[i]);
                        if(!isOK) break;        //1個でもNGなら break
                    }
                }

                if(isOK) {
                    $(_upload_selector).show();     //アップロードボタン表示
                } else {
                    $(_upload_selector).hide();     //アップロードボタン非表示
                }
            }
        }

        /**
         * 最大ファイル添付数に達した時に呼び出されるイベントハンドラ
         */
        function checkReachHandler(file) {
            $(_upload_selector).hide();     //アップロードボタン非表示
        }

        return uiDropzone;  //Dropzoneインスタンスを返す
    }

});