読者です 読者をやめる 読者になる 読者になる

MATz Tech

webの技術的なことを中心にいろいろと

MEANスタックとMERNスタック

先日、Node.jsの勉強会に行ってきたので覚え書き。

今までJavaScriptはクライアントサイドのjQueryくらいしか経験がなく、サーバ側は触ったことなかったんですが、たまたま近所で勉強会やってたんで行ってきました。

いろんな話聞けたのですが、その中で印象深かったMEANスタックとMERNスタックについてです。

「MEAN」「MERN」というのは、所謂「XAMPP」みたいな言語やDBを組み合わせた開発環境のアーキテクチャです。

MEAN

M:MongoDB

ドキュメント指向のDBで、RDMSではなくNoSQLに分類されます。

E:Express

Node.jsのMVCフレームワークです。

A:AngularJS

JavaScriptのクライアントサイドのMVWフレームワークです。

N:Node.js

サーバーサイドのJavaScript環境です。

MERN

M:MongoDB

E:Express

R:React.js

Facebook製のUI構築ライブラリです。フレームワークではありません。

N:Node.js



結局、AngularJSとReact.jsどっちを使うねんということです。

ちなみに、Google Trendsはこんな感じでした。

f:id:MATz:20170410003853p:plain

f:id:MATz:20170410004351p:plain

う〜ん、圧倒的!

色々調べてみたら、チャット機能みたいにリアルタイム性が強いならReact.jsが向いてるようです。
そうでなければAngularJSが良さげです。

また、AngularJSはhtml主体で、React.jsはJavaScriptが主体。
私のようにjQueryに慣れてる場合は、AngularJSの方が取っ付きやすそうですね。

勉強会でも、AngularJSオススメの人がたくさんいました。

というわけで、Node.js始めるならMEANからやってみようかなと思っている今日この頃。

ファイル選択した画像をモーダル表示

前回に引き続き、画像のファイル選択ネタです。

今回は、ファイル選択した画像をモーダルウインドウで表示します。
jQueryプラグインでこれ系のはけっこうあるんですけど、敢えてプラグインに頼らずやってみました!


htmlとcssはこんな感じです。

<div id="modal-content">
    <img src="" id="preview" width="500px" />
    <a id="modal-cancel" class="button-link">cancel</a>
    <a id="modal-ok" class="button-link">OK</a>
</div>
<input type="file" id="imageSelect" onChange="imgSelect();" />
/*モーダルウィンドウ*/
#modal-content{
    width:50%;
    margin:1.5em auto 0;
    padding:10px 20px;
    border-radius: 10px;
    background:#fff;
    z-index:2;
    position:fixed;
    display:none;
}
.button-link{
    color:#00f;
    text-decoration:underline;
}
.button-link:hover{
    cursor:pointer;
    color:#f00;
}
#modal-overlay{
    z-index:1;
    display:none;
    position:fixed;
    top:0;
    left:0;
    width:100%;
    height:120%;
    background-color:rgba(0,0,0,0.75);
}



そしてjsはこちら!

まず、全体をグレーで覆って、その上にモーダルウィンドウを表示します。
そして、imgタグに選択した画像を設定して表示すればOK!シンプルですね!

OKボタン・背景グレー・cancelボタンクリックの場合の処理は、背景グレーとモーダルウィンドウ非表示にするだけなんで基本的には全て同じなんですが、cancelクリックの時だけファイル選択クリア入れてます。

function imgSelect() {
    var file = $("#imageSelect").prop("files")[0];
                
    //画像ファイルかチェック
    if (file["type"] != "image/jpeg" && file["type"] != "image/png" && file["type"] != "image/gif") {
        alert("画像ファイルを選択してください");
        //ファイル選択クリア
        $("#imageSelect").val('');

    } else {
        var fr = new FileReader();

        fr.onload = function() {
            //背景グレーにしてモーダルウィンドウを表示
            if ($("#modal-overlay")[0]) {
                $("#modal-overlay").remove();
            }
            $("body").append('<div id="modal-overlay"></div>');
            $("#modal-overlay").fadeIn("slow");
            $("#modal-content").fadeIn("slow");
                        
            //選択した画像をimgタグに表示
            $("#preview").attr("src", fr.result);
                        
            //OKボタンまたは背景のグレーをクリックした場合
            $("#modal-ok, #modal-overlay").unbind().click(function () {                            
                //モーダルウィンドウ非表示にして、背景グレーも非表示
                $("#modal-content, #modal-overlay").fadeOut("slow", function () {
                    $("#modal-overlay").remove();
                });
            });
                        
            //cancelボタンクリックした場合
            $("#modal-cancel").unbind().click(function () {
                //モーダルウィンドウ非表示にして、背景グレーも非表示
                $("#modal-content, #modal-overlay").fadeOut("slow", function () {
                    $("#modal-overlay").remove();
                });
                //ファイル選択クリア
                $("#imageSelect").val('');
            });
        };

        fr.readAsDataURL(file);
    }
}   

【修正あり】jsとcanvasで画像の容量を落とす

【2017/4/2修正】
phpのソース載せてなかったので、追記しました。

phpで画像アップロードしたいけど、最近のiPhoneのカメラとか画質良いので容量オーバーしちゃいますよね。

まあphp.iniで「post_max_size」とか触ればいいんですけど、なんせ重いです。

それなら、クライアントで容量落としてから上げれば良いじゃん!ってことでこちらの方法!


htmlはこんな感じです。

<form action="" method="post" id="imageForm">
    <img src="" id="preview" />
    <canvas id="canvas"></canvas>
    <input type="file" id="imageSelect" onChange="canvasDraw();" />
    <input type="button" onClick="imageUpload();" value="アップロード" />
</form>



まず、input type="file"で選択した画像を、一旦img要素に原寸サイズで表示します。
原寸サイズで表示して全体のレイアウト崩れるの嫌なら、hiddenにすればOKです。

次に、img要素から画像をimageオブジェクトとして取得して、canvas要素に描画します。
描画時にサイズ縮小すると、これだけで結構容量下がります。
今回は横幅800px固定にして、縦横比保ったまま縮小してます。

ちなみに、canvas要素もhidden入れたら非表示にできます。

function canvasDraw() {
    var file = $("#imageSelect").prop("files")[0];
                
    //画像ファイルかチェック
    if (file["type"] != "image/jpeg" && file["type"] != "image/png" && file["type"] != "image/gif") {
        alert("画像ファイルを選択してください");
        $("#imageSelect").val(''); //選択したファイルをクリア

    } else {
        var fr = new FileReader();

        fr.onload = function() {
            //選択した画像を一旦imgタグに表示
            $("#preview").attr('src', fr.result);
                        
            //imgタグに表示した画像をimageオブジェクトとして取得
            var image = new Image();
            image.src = $("#preview").attr('src');
                        
            //縦横比を維持した縮小サイズを取得
            var w = 800;
            var ratio = w / image.width;
            var h = image.height * ratio;
                        
            //canvasに描画
            var canvas = $("#canvas");
            var ctx = canvas[0].getContext('2d');
            $("#canvas").attr("width", w);
            $("#canvas").attr("height", h);
            ctx.drawImage(image, 0, 0, w, h);      
        };

        fr.readAsDataURL(file);
    }
}   



そして、アップロードボタンクリックしたタイミングで、canvasデータの画質を落としてajaxでPOST送信します。

ここではtoDataURLでバイナリ化しますが、この時に第二引数で画質が指定できるんです。
0.0〜1.0の数値を指定して、好きな画質に落としましょう。

今回は、2MB以下に落とします。
まず最初は、第二引数を指定せずオリジナル容量(画質を落としていない場合の容量)を取得します。
次に、取得したオリジナル容量から2MB以下に落とすための数値を算出して、指定しています。

最後に、ajaxでPOST送信したら完了!

function imageUpload() {
    var form = $("#imageForm").get(0);
    var formData = new FormData(form);

    //画像処理してformDataに追加
    if ($("#canvas").length) {
        //canvasに描画したデータを取得
        var canvasImage = $("#canvas").get(0);
                    
        //オリジナル容量(画質落としてない場合の容量)を取得
        var originalBinary = canvasImage.toDataURL("image/jpeg"); //画質落とさずバイナリ化
        var originalBlob = base64ToBlob(originalBinary); //オリジナル容量blobデータを取得
        console.log(originalBlob["size"]);
                    
        //オリジナル容量blobデータをアップロード用blobに設定
        var uploadBlob = originalBlob;                    
                    
        //オリジナル容量が2MB以上かチェック
        if(2000000 <= originalBlob["size"]) {
            //2MB以下に落とす
            var capacityRatio = 2000000 / originalBlob["size"];
            var processingBinary = canvasImage.toDataURL("image/jpeg", capacityRatio); //画質落としてバイナリ化
            uploadBlob = base64ToBlob(processingBinary); //画質落としたblobデータをアップロード用blobに設定
            console.log(capacityRatio);                        
            console.log(uploadBlob["size"]);
        }
                    
        //アップロード用blobをformDataに追加
        formData.append("selectImage", uploadBlob);
    }

    //formDataをPOSTで送信
    $.ajax({
        async: false,
        type: "POST",
        url: "upload.php",
        data: formData,
        dataType: "text",
        cache: false,
        contentType: false,
        processData: false,
        error: function (XMLHttpRequest) {
            console.log(XMLHttpRequest);
            alert("アップロードに失敗しました");
        },
        success: function (res) {
            if(res !== "OK") {
                console.log(res);
                alert("アップロードに失敗しました");
            } else {
                alert("アップロードに成功しました");
            }
        }
    });
}

// 引数のBase64の文字列をBlob形式にする
function base64ToBlob(base64) {
    var base64Data = base64.split(',')[1], // Data URLからBase64のデータ部分のみを取得
          data = window.atob(base64Data), // base64形式の文字列をデコード
          buff = new ArrayBuffer(data.length),
          arr = new Uint8Array(buff),
          blob,
          i,
          dataLen;
    // blobの生成
    for (i = 0, dataLen = data.length; i < dataLen; i++) {
        arr[i] = data.charCodeAt(i);
    }
    blob = new Blob([arr], {type: 'image/jpeg'});
    return blob;
}            



upload.phpは、シンプルにPOSTで受けた画像をアップロードしてるだけです。
バイナリ化するときにjpgで指定してるので、保存するファイルの拡張子はjpg固定です。

<?php
try{
    if(!move_uploaded_file($_FILES["selectImage"]["tmp_name"], 'test.jpg')){
        throw new Exception('画像ファイルアップロードエラー!');
    }
    
    echo 'OK';
} catch (Exception $ex) {
    echo $ex->getMessage();
}
?>



なので、この方法だと出来上がる画像はjpg固定になってしまいます。
pngやgifにする方法あれば教えてくださいm(_ _)m


<参照>
‘input type=file’から’canvas’への転写(画像の引き伸ばし対処) – GUNMA GIS GEEK
toDataURL() メソッド - Canvasリファレンス - HTML5.JP
canvasで描いた絵をバイナリ形式でサーバーにPOST送信する方法 | while(isプログラマ)