LoginSignup
105
105

More than 5 years have passed since last update.

スマホ用のクールなアップローダーを作る

Last updated at Posted at 2015-02-09

コンセプトは6つ

・簡単にアップロードできる
・クライアントサイドで画像のサムネイルを作る
・複数のファイルをアップロードできる
・画像をソートしてアップロードできる
・スマホの写真が時々回転してしまうので対策しよう
・そのままスマホの写真を複数アップロードできるようにすると
 ファイルサイズがえらいこと(1枚4M!)になったので
 クライアント側で画像を縮小
・もちろんAjaxでアップロードだ!

こんなアップローダーを作りたいの。でも、きっと夢ね…。

偉大なるプログラマーに敬意を!

このアップローダーを作るにあたり以下のプログラムを使わせてもらった。

@stomita
megapix-image.js
https://github.com/stomita/ios-imagefile-megapixel

@jseidelin
exif-js
https://github.com/jseidelin/exif-js

@ibnRubaXa
Sortable.js
http://rubaxa.github.io/Sortable/

使い方については以下の解説が役立った。
@wada811
http://wada811.blogspot.com/2014/11/image-upload--preview.html

もちろんキミへの感謝も忘れないよ!
jQuery
http://jquery.com/

index.js
    IMAGES = [];
    function resizeImage(file, no) {
        var d = new $.Deferred();
        var mpImg = new MegaPixImage(file);
        var src_keeper = document.getElementById('src_keeper');
        EXIF.getData(file, function() {
            var orientation = file.exifdata.Orientation;
            var mpImg = new MegaPixImage(file);
            mpImg.render(src_keeper, { maxWidth: 1024, orientation: orientation }, function() {
                var resized_img = $(src_keeper).attr('src');
                d.resolve(resized_img, no);
            });
        });
        return d.promise();
    }

    var sortable;

さて、まずはアップローダーの共通コア部分から解説しよう!

・縮小画像のデータを格納するIMAGES
・ソート機能を使うためのsortableをこっそり定義

ここで実現しているのは
・クライアント側での縮小
・EXIF情報を使って画像の回転の修正
の2つ。

画像が完全に読み込まれてから処理をする必要があり、
処理が完全に終わってから次に進むようにする。
$.Deferredの出番だ!

だいたいの部分は上記解説サイトのままだね。

iPhoneは写真の複数選択ができた。
だからHTML部分はこう書いておくよ。

index.html
<input id="file" type="file" multiple accept="image/jpeg, image/gif, image/png">

JavaScript部分はこうだ!

index.js
    $('#file').on('change', function() {
        var files_length = this.files.length; 
        for (var i=0; i<files_length; i++) {
            var file = this.files[i];
            resizeImage(file, i).then(function(resize_image, no) {
                var img = $('<img>');
                $(img).css('width', '100px');
                img.attr('id', no);
                img.attr('src', resize_image);
                var canvasData = resize_image.replace(/^data:image\/jpeg;base64,/, '');
                IMAGES.push(canvasData);
                $('#img').append(img);
                // 全ての画像を読み込んだら以下の処理を実行
                if ($('#img').find('img').size() == files_length) {
                    if (files_length > 1) {
                        sortable = new Sortable(document.getElementById('img'), {
                            group: 'photo',
                            animation: 150
                        });
                    }
                }
            });
        };
    });

縮小が終わったらIMAGESに入れておく。
全ての画像が読み込まれたらソートできるようにする。
どうしてここの処理で$.Deferredを使わないかって?
おっと、次の説明だ!

index.js
    $('#upload_button').on('click', function() {
        var fd = new FormData();
        $('#img').find('img').each(function() {
            var i = parseInt($(this).attr('id'));
            fd.append('base64_encoded_data[]', IMAGES[i]);
        });
        $.ajax({
            url: '/index.php',
            type: 'POST',
            data: fd,
            processData: false,
            contentType: false,
            dataType: 'json'
        })
        .done(function(data) {
        });
    });

FormDataを使ってフォームデータを動的に作成している。
サーバ側にはbase64エンコードされたデータが渡るってことさ。

さてAndroid版だ!

なんで上でiPhoneだって前置きをしたかというと
Androidでは写真の複数選択ができなかったんだ(ouch!)
なので1枚ずつ選択していくよ。

HTML部分

index.html
<input class="submit" id="android_file_select" type="button" value="画像を追加">

JavaScript部分

index.js
    $('#android_file_select').click(function() {
        var hookInput = $('<input type="file" accept="image/jpeg, image/gif, image/png" name="files[]" style="display:none;">');
        var id = 'i' + parseInt((new Date)/1000);
        hookInput.attr('id', id);
        $('#android_file_upload').append(hookInput);
        $('#'+id).click();
        $(hookInput).on('change', function() {
            var i = IMAGES.length;
            var file = this.files[0];
            resizeImage(file, i).then(function(resize_image, no) {
                var img = $('<img>');
                $(img).css('width', '100px');
                img.attr('id', no);
                img.attr('src', resize_image);
                var canvasData = resize_image.replace(/^data:image\/jpeg;base64,/, '');
                IMAGES.push(canvasData);
                $('#android_img').append(img);
                if (sortable) {
                    sortable.destroy();
                }
                sortable = new Sortable(document.getElementById('android_img'), {
                    group: 'photo',
                    animation: 150
                });
            });
        });
    });

上に書いた理由からHTMLとJavaScriptの処理自体は
HTTP_USER_AGENTでiPhoneとAndroidに分けざるを得なかった。
画像に対応するinputを動的に作成しているところ以外は
iPhone版と同じかな。

index.js
    $('#android_upload_button').click(function() {
        var fd = new FormData();
        $('#android_img').find('img').each(function() {
            var i = parseInt($(this).attr('id'));
            fd.append('base64_encoded_data[]', IMAGES[i]);
        });
        $.ajax({
            url: '/index.php',
            type: 'POST',
            data: fd,
            processData: false,
            contentType: false,
            dataType: 'json'
        })
        .done(function(data) {
        });
    });

さぁ、これでクールなアップローダーの完成だ!

今回のこの知識がキミの役に立つといいな。

105
105
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
105
105