アップロード付きシンプル画像ギャラリー

# 概要
以前の投稿[PHPを用いたシンプルな画像ギャラリー](http://qiita.com/michimaru/items/767aacdedbd7f0a95d2a)にファイルのアップロード機能を追加してみた。
# 基本方針
ファイルのアップロードはajaxとFormDataオブジェクトを用いる。
フォームのactionで別ページに飛ぶのは避けたかったので色々調べた結果、ajaxを使用することで実現できる事が分かった。
# 実際の手順
## 1. 作成htmlの構成
HTML5で書いていきます。
※note1：jQuery.jsとlightbox用cssへのリンクが入ります。
※note2：type="file"のinputフォームを用います。
※note3：ギャラリーを表示する為の空のブロックです。
※note4：lightbox用JSは\</body>の直前に配置します。

```sceleton.html
<!DOCTYPE HTML>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <script src="http://code.jquery.com/jquery-latest.js" type="text/javascript"></script>
        <link rel="stylesheet" href="./css/lightbox.css">   <!-- note1 -->
        <title>画像アップロードギャラリー</title>
    </head>
    <body>
        <section id="upform" class="upform">
            <h1>アップロードする画像を選択してください</h1>
            <form id="upload-form">
                <input id="ufiles" type="file" name="ufiles[]" accept="image/*" multiple> <!-- note2 -->
            </form>
            <br>
            <hr>
        </section>
        <section id="gallery" class="gallery">
            <h1>アップロード画像一覧</h1>
            <h3>クリックすると拡大表示されます</h3>
            <div id="views" class="views"></div> <!-- note3 -->
        </section>

       <script>
         <!-- ここにjQueryスクリプトを追加して行きます -->
       </script>
       <script type="text/javascript" src="./js/lightbox.js"></script> <!-- note4 -->
    </body>
</html>
```

## 2. jQueryスクリプトの内容
※note5：function dmout()は[jQuery.get()](http://js.studio-kingdom.com/jquery/ajax/get)でシンプルなGETリクエストをサーバに送っています。サーバ側で"gallery.php"を実行し、結果を指定のHTML要素(ここではclass="views")へ出力します。ページがロードされた時とjQuery.ajax()が成功した時に呼ばれて実行されますが、これは他の良い方法を探した方が良いかも。
※note6：[FormData](https://developer.mozilla.org/ja/docs/Web/API/FormData)オブジェクトを作成しています。
newでインスタンスした後にappendメソッドで要素を追加しています。
複数ファイルを一度にアップロード出来るようinputフォームにmuliple属性を付加しているので、[jQuery.each()](http://semooh.jp/jquery/api/utilities/jQuery.each/object%2C+callback/)を使用して全てのfile要素を順に追加しています。
※node7：[jQuery.ajax()](http://js.studio-kingdom.com/jquery/ajax/ajax)でAJAX通信を行います。FormDataを送信しhtmlを受け取ります。processDataとcontentTypeはfalseにする必要が有るとの事です。通信が正常に行われると.done(function())がコールバックされます。

```jquery.script
        $(function(){
            // note5 ページロード時の呼び出し
            domout("gallery.php", ".views");

           // id="ufile"の変化でコールバック
            $("#ufiles").change(function(){
                // 選択ファイルの有無をチェック
                if (!this.files.length) {
                    alert('ファイルが選択されていません');
                    return;
                }

                // note6 FormDataを用意
                var fd = new FormData();
                fd.append('action','upload-form');
                $.each($("input[type='file']")[0].files, function(i, file) {
                    fd.append('ufiles['+i+']', file);
                });

                // note7 ajaxでFromDataを送信
                $.ajax({
                    url: 'uploader_m.php',
                    type: 'POST',
                    processData: false,
                    contentType: false,
                    dataType: 'html',
                    data: fd
                }).done(function(data, textStatus, jwXHR){
                    alert(data);
                    // note5 終了時の呼び出し
                    domout("gallery.php", ".views");
                }).fail(function(jqXHR, textStatus, errorThrown){
                    alert('エラーが発生しました : ' + textStatus
                        + "\nHTTP status : " + errorThrown);
                });
            });
        });

        // note5 DOMへhtmlを出力
        function domout(_url, _html) {
            $.get(_url, function(data) {
                $(_html).html(data);
            });
        }
```
## 3. html全体
こんな感じです。
cssでサムネイルをちょこっと修飾しています。

```index.html
<!DOCTYPE HTML>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <script src="http://code.jquery.com/jquery-latest.js" type="text/javascript"></script>
        <!-- lightbox用CSS -->
        <link rel="stylesheet" href="./css/lightbox.css">
        <title>画像アップロードギャラリー</title>
        <!-- サムネイルを見やすく -->
        <style type="text/css"">
         .image-link {
             display: inline-block;
             padding: 2px;
             margin: 0 0.5rem 1rem 0.5rem;
             background-color: #fff;
             line-height: 0;
             border-radius: 4px;
             transition: background-color 0.5s ease-out; }
         .image-link:hover {
             background-color: #4ae;
             transition: none; }
        </style>
    </head>
    <body>
        <section id="upform" class="upform">
            <h1>アップロードする画像を選択してください</h1>
            <form id="upload-form">
                <input id="ufiles" type="file" name="ufiles[]" accept="image/*" multiple>
            </form>
            <br>
            <hr>
        </section>
        <section id="gallery" class="gallery">
            <h1>アップロード画像一覧</h1>
            <h3>クリックすると拡大表示されます</h3>
            <div id="views" class="views"></div>
        </section>

       <script>
        $(function(){
            //ページロード時の呼び出し
            domout("gallery.php", ".views");

           // id="ufile"の変化でコールバック
            $("#ufiles").change(function(){
                // 選択ファイルの有無をチェック
                if (!this.files.length) {
                    alert('ファイルが選択されていません');
                    return;
                }

                // FormDataを用意
                var fd = new FormData();
                fd.append('action','upload-form');
                $.each($("input[type='file']")[0].files, function(i, file) {
                    fd.append('ufiles['+i+']', file);
                });

                // ajaxでFromDataを送信
                $.ajax({
                    url: 'uploader_m.php',
                    type: 'POST',
                    processData: false,
                    contentType: false,
                    dataType: 'html',
                    data: fd
                }).done(function(data, textStatus, jwXHR){
                    alert(data);
                    //終了時の呼び出し
                    domout("gallery.php", ".views");
                }).fail(function(jqXHR, textStatus, errorThrown){
                    alert('エラーが発生しました : ' + textStatus
                        + "\nHTTP status : " + errorThrown);
                });
            });
        });

        // DOMへhtmlを出力
        function domout(_url, _html) {
            $.get(_url, function(data) {
                $(_html).html(data);
            });
        }

       </script>
       <!-- lightbox用JS -->
       <script type="text/javascript" src="./js/lightbox.js"></script>
    </body>
</html>
```

## 4. ファイルアップロードするPHP
POSTされたファイル情報は$\_FILESでPHPから参照できます。サーバ上では受信ファイルが"tmp_name"に保存されているので、アップロード先にmove_upload_file()で移動させています。アップロード先のディレクトリの書き込み権限に注意です。

```upload_m.php
<?php
define('FILE_PATH','./upload/'); //保存するパスを指定

if ( !empty($_FILES) ) {
    for ( $i=0; $i<count($_FILES["ufiles"]["tmp_name"]); $i++ ) {
        // アップロードされたファイルが有る事を確認
        if (is_uploaded_file($_FILES["ufiles"]["tmp_name"][$i])) {
            $saveFilename = FILE_PATH . $_FILES['ufiles']['name'][$i];

            // ファイルの移動
            if (move_uploaded_file($_FILES["ufiles"]["tmp_name"][$i], $saveFilename)) {
                echo $_FILES["ufiles"]["name"][$i], "をアップロードしました\n";
            } else {
                echo $_FILES["ufiles"]["name"][$i], "アップロードエラー\n";
            }
        }
    }
}
?>
```
## 5. ギャラリー用PHP
基本的に[PHPを用いたシンプルな画像ギャラリー](http://qiita.com/michimaru/items/767aacdedbd7f0a95d2a)で使用したPHPと同じ内容ですが、ここではgetPictures()を呼んでおかないと実行されません。

``` gallery.php
<?php
$img_dir = './upload/';

function getPictures() {
    global $img_dir;
    $i = 0;
    if ( $handle = opendir($img_dir) ) {
        while ( ($file = readdir($handle)) !== false ) {
            $path = $img_dir . $file;
            if ( !is_dir($path) ) {
                $split = explode('.', $file);
                $name = $split[0];
                $ext = $split[count($split) - 1];
                if ( ($type = getPictureType($ext)) == '' ) {
                    continue;
                }
                echo '<a class="image-link" href="'.$path.'" data-lightbox="imgs" data-title="'.$name.'"><img class="image" src="'.$path.'" width="120" alt="'.$file.'">';
            }
        }
    }
}

function getPictureType($ext) {
    if ( preg_match('/jpg|jpeg/i', $ext) ) {
        return 'jpg';
    } else if ( preg_match('/png/i', $ext) ) {
        return 'png';
    } else if ( preg_match('/gif/i', $ext) ) {
        return 'gif';
    } else {
        return '';
    }
}

getPictures();

?>
```
# 結果
以下の環境で意図した動作を確認。
Edge 25.10586.0.0では上手く動作しなかったorz
サーバ情報
$ uname -a
Linux raspi-007 4.4.10-v7+ #885 SMP Fri May 13 15:44:27 BST 2016 armv7l GNU/Linux

$  php -version
PHP 5.6.20-0+deb8u1 (cli) (built: Apr 28 2016 00:01:26)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2016, by Zend Technologies

$ apachectl -v
Server version: Apache/2.4.10 (Raspbian)
Server built:   Jan 23 2016 22:26:49

ブラウザ情報
FireFox 46.0.1
Chrome 51.0.2704.79 m
# 参考にさせて頂いたWEBサイト
http://chaika.hatenablog.com/entry/2014/04/12/001012
http://qiita.com/BRS_matsuoka/items/ba79a2f500a10ddc0923
http://stackoverflow.com/questions/12989442/uploading-multiple-files-using-formdata