ドラッグ&ドロップでファイルアップロードする

ドラッグ&ドロップでファイルアップロードする

こんにちは山崎です。久しぶりの投稿になります。

今回はJavascriptでドラッグアンドドロップでのファイルアップロードを実装しようと思います。

完成形・仕様

このような画面になります。ファイルはドラッグ&ドロップもしくは、ファイル選択フィールドからアップロードし、下にアップロードされた画像を表示する形です。 Ajaxなどを使ってアップロードされた時点でサーバーに保存するのではなく、送信ボタンが押された時点でサーバーに画像がアップロードされる仕様です。 今回はスタイルを最低限しか当ててないため質素ですが、こういったUIはCMSの管理画面などでよく見ると思います。

スクリーンショット 2020-03-30 17.02.21.png

実装

まずHTMLを作っていきます。今回は最低限のスタイルで済ませるので、インラインでスタイルを書いています。

<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ドラッグ&ドロップでファイルをアップロード</title>
</head>
<body>
<h1>画像アップロード</h1>
<form action="./upload.php" method="post" enctype="multipart/form-data">
    <div id="drop-zone" style="border: 1px solid; padding: 30px;">
        <p>ファイルをドラッグ&ドロップもしくは</p>
        <input type="file" name="file" id="file-input">
    </div>
    <h2>アップロードした画像</h2>
    <div id="preview"></div>
    <input type="submit" style="margin-top: 50px">
</form>
</body>
</html>

まず、#drop-zoneというのがファイルのドロップが有効な範囲です。 そして、h2の下の#previewが、アップロードしたファイルのプレビューが見れる場所になります。input[type="file"]にもIDを振っておきます。

次にJavascriptを書いていきます。

<script type="text/javascript">

    var dropZone = document.getElementById('drop-zone');
    var preview = document.getElementById('preview');
    var fileInput = document.getElementById('file-input');

    dropZone.addEventListener('dragover', function(e) {
        e.stopPropagation();
        e.preventDefault();
        this.style.background = '#e1e7f0';
    }, false);

    dropZone.addEventListener('dragleave', function(e) {
        e.stopPropagation();
        e.preventDefault();
        this.style.background = '#ffffff';
    }, false);

    fileInput.addEventListener('change', function () {
        previewFile(this.files[0]);
    });

    dropZone.addEventListener('drop', function(e) {
        e.stopPropagation();
        e.preventDefault();
        this.style.background = '#ffffff'; //背景色を白に戻す
        var files = e.dataTransfer.files; //ドロップしたファイルを取得
        if (files.length > 1) return alert('アップロードできるファイルは1つだけです。');
        fileInput.files = files; //inputのvalueをドラッグしたファイルに置き換える。
        previewFile(files[0]);
    }, false);

    function previewFile(file) {
        /* FileReaderで読み込み、プレビュー画像を表示。 */
        var fr = new FileReader();
        fr.readAsDataURL(file);
        fr.onload = function() {
            var img = document.createElement('img');
            img.setAttribute('src', fr.result);
            preview.innerHTML = '';
            preview.appendChild(img);
        };
    }
</script>

まず、今回JSで使うDOMを取得して変数に入れます。

var dropZone = document.getElementById('drop-zone');
var preview = document.getElementById('preview');
var fileInput = document.getElementById('file-input');

ドラッグして、#drop-zoneの範囲に入った場合分かりやすく色が変わるようにイベントリスナーを指定します。

dropZone.addEventListener('dragover', function(e) {
  e.stopPropagation();
  e.preventDefault();
  this.style.background = '#e1e7f0';
}, false);

dropZone.addEventListener('dragleave', function(e) {
  e.stopPropagation();
  e.preventDefault();
  this.style.background = '#ffffff';
}, false);
fileInput.addEventListener('change', function () {
  previewFile(this.files[0]);
});

dropZone.addEventListener('drop', function(e) {
        e.stopPropagation();
        e.preventDefault();
        this.style.background = '#ffffff'; //背景色を白に戻す
        var files = e.dataTransfer.files; //ドロップしたファイルを取得
        if (files.length > 1) return alert('アップロードできるファイルは1つだけです。');
        fileInput.files = files; //inputのvalueをドラッグしたファイルに置き換える。
        previewFile(files[0]);
}, false);

function previewFile(file) {/* FileReaderで読み込み、プレビュー画像を表示。 */
  var fr = new FileReader();
  fr.onload = function() {
    var img = document.createElement('img');
    img.setAttribute('src', fr.result);
    preview.innerHTML = '';
    preview.appendChild(img);
  };
  fr.readAsDataURL(file);
}

そして最後にファイルをドラッグ&ドロップした時とファイルをinputボタンからアップロードした時の処理を書いていきます。

どちらのイベントもプレビュー機能の処理は一緒なので、プレビュー機能はpreviewFileという関数にまとめました。

var fr = new FileReader();

まずFileReaderインスタンスを作成します。

fr.onload = function() {
    var img = document.createElement('img');
    img.setAttribute('src', fr.result);
    preview.innerHTML = '';
    preview.appendChild(img);
};

次にファイルの読み込みが終わったタイミングで実行する関数を以下のように定義します。 fr.resultでファイルのパスを読みこむことができます。

fr.readAsDataURL(file);

そして引数で受け取ったファイルを読み込みます。 ファイルの読み込みが完了したら、先ほどfr.onloadにセットした関数が実行され、#previewに入ります。

fileInput.addEventListener('change', function () {
  previewFile(this.files[0]);
});

次にinput[type="file"]でアップロードしたらchangeイベントが発火されるので、先ほど定義したpreviewFileを実行します。filesプロパティは配列になっているので、index番号0のファイルを引数に指定します。(※今回は複数のファイルアップロードはできない仕様にしています。)

dropZone.addEventListener('drop', function(e) {
    e.stopPropagation();
    e.preventDefault();
    this.style.background = '#ffffff'; //背景色を白に戻す
    var files = e.dataTransfer.files; //ドロップしたファイルを取得
    if (files.length > 1) return alert('アップロードできるファイルは1つだけです。');
    fileInput.files = files; //inputのvalueをドラッグしたファイルに置き換える。
    previewFile(files[0]);
}, false);

次に#dropzoneのイベントリスナーを定義します。 コールバック関数の上の2行はドロップイベントのブラウザのデフォルトの挙動を防ぐために記述しています。

var files = e.dataTransfer.files;

ドロップしたファイルリストはイベントオブジェクトのdataTransfer.filesプロパティで取得することができます。

fileInput.files = files;

次に#file-inputにドロップしたファイルをセットします。 fileInput.value = ....;とつい書いてしまいそうですが、input[type="file"]の場合はfilesプロパティにセットしなければいけないようです。 これでドロップしたファイルがinputにセットされました。

サーバーに画像をアップロードする

あとはドラッグアンドドロップでアップロードした画像を試しに送信してみます。

<?php
$tempfile = $_FILES['file']['tmp_name'];
$filename = './img/'.$_FILES['file']['name'];

if (is_uploaded_file($tempfile)) {
    if (move_uploaded_file($tempfile, $filename)) {
        echo $filename . "をアップロードしました。";
    }
}
?>

先ほどのHTMLファイルと同じ階層にupload.phpを作成します。ローカルサーバーを起動してアップロードしてみてください。imgディレクトリに画像がアップロードできたかと思います。

以上です。

  • このエントリーをはてなブックマークに追加

この記事を読んだ人にオススメ