勉強したことのメモ

Webエンジニア / プログラマが勉強したことのメモ。

summernoteで画像選択時にサーバにファイルをアップロードする方法

summernoteで画像を選択した際、ファイルはアップロードされずBase64エンコードされた値がエディタ内に挿入される。これを画像ファイルがサーバにアップロードされるようにしたい。また、サイズ圧縮のためJPG / PNGファイルの場合はWebP画像に変換したい。以下に対応方法をメモ。

 

summernoteについて

summernote自体の使用方法については過去記事参照。

 

対応方法

WYSIWYGエディタ側(index.php)

<form action="./" method="post" enctype="multipart/form-data">
    <div>
        <label>
            <input type="radio" value="on" name="switching" checked>On
        </label>
        <label>
            <input type="radio" value="off" name="switching">Off
        </label>
    </div>
    <textarea name="editor" id="editor"></textarea><br>
    <input type="submit" value="送信">
</form>

<script type="text/javascript" src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.20/dist/summernote-lite.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.20/lang/summernote-ja-JP.min.js" type="text/javascript"></script>

<script>
$(function(){
    const selector = '#editor';

    //summernoteの開始
    startSummernote(selector);

    //summernoteのOn / Off切り替え
    $('input[name="switching"]').on('change', function(){
        if( $(this).val() == 'on' ){

            //summernoteの開始
            startSummernote(selector);
        }else{

            //空の場合はsummernote側で<p><br></p>が挿入されるので削除
            if( $(selector).summernote('code') == '<p><br></p>' ){
                $(selector).val('');
            }

            //summernoteの破棄
            $(selector).summernote('destroy');
        }
    });

    //summernoteの開始用関数
    function startSummernote( selector ){
        if( !selector ) return false;

        //オプション設定
        const summernote_option = {
            lang: 'ja-JP',
            height: 300,
            focus: true,
            callbacks: {
                onImageUpload: function( file ){
                    uploadImg( file[0], selector );
                },
            }
        }

        $(selector)
            //オプションを指定して初期化
            .summernote(summernote_option)

            //エンタークリック時に<br>タグを挿入
            .on('summernote.enter',
                function(we, e){
                    $(this).summernote('pasteHTML', '<br>&ZeroWidthSpace;');
                    e.preventDefault();
                }
            );
    }

    function uploadImg( file, selector ){

        if( !file || !selector ){
            alert('エラーが発生しました。');
            return false;
        }

        if( !file.type.match(/^image/) ){
            alert('画像を選択してください');
            return false;
        }

        const limit_size = 5;
        if( file.size > limit_size * 1024 * 1024 ){
            alert(`画像サイズは${limit_size}MBまでです`);
            return false;
        }

        data = new FormData();
        data.append('file', file);
        $.ajax({
            data: data,
            type: 'POST',
            url: './upload.php',
            cache: false,
            contentType: false,
            processData: false,
        }).done(function(data, textStatus, jqXHR){
            const result = JSON.parse(data);

            if( !result.uploaded || !result.url ){
                const msg = ( result.message ) ? result.message : 'エラーが発生しました。';
                return false;
            }

            $(selector).summernote('insertImage', result.url);
        });
    }

});
</script>

画像アップロード側(upload.php)

<?php
do{

    //アップロード先のパス
    $path = '/path/to/img/';

    //アップロード先のURL
    $url = 'https://test.com/path/to/img/';

    $img = $_FILES['file'];

    if( !$img['size'] ){
        $msg = '画像がアップロードされていません。';
        break;
    }

    $ext = $img['type'];
    if ($ext == 'image/jpeg' || $ext == 'image/jpg') {
        $tmp_ext = '.jpg';
    } elseif ($ext == 'image/png') {
        $tmp_ext = '.png';
    } elseif ($ext == 'image/gif') {
        $tmp_ext = '.gif';
    } elseif ($ext == 'image/webp') {
        $tmp_ext = '.webp';
    }

    if( !$tmp_ext ){
        $msg = 'jpg / png / gif / webpファイルのみアップロードいただけます。';
        break;
    }

    $img_name = md5(uniqid(rand(), true)) . '_' . time();

    if( !move_uploaded_file($img['tmp_name'], $path . $img_name . $tmp_ext) ){
        $msg = 'アップロード中にエラーが発生しました。';
        break;
    }

    $img = $img_name . $tmp_ext;

    if( $tmp_ext == '.jpg' ){
        $old_img = imagecreatefromjpeg($path . $img);
    }else if( $tmp_ext == '.png' ){
        $old_img = imagecreatefrompng($path . $img);
    }else{
        break;
    }
    if( imagewebp($old_img, $path . $img_name . '.webp') ){
        $img = $img_name . '.webp';
    }

    $flg = true;

}while(0);

if( $msg ){
    $response = array(
        'uploaded' => false,
        'error' => array(
            'message' => $msg,
        ),
    );
}else{
    $response = array(
        'url' => $url . $img, 
        'uploaded' => true,
    );
}

echo json_encode($response);

注意点

WebP変換にはPHPのGDライブラリが必要になる。レンタルサーバだと大丈夫だと思うが、VPSとかの場合は必要に応じてインストールすること。

画像のstyleを指定したい場合

$(selector).summernote('insertImage', result.url);

で画像を出力すると以下のようにwidthが自動で指定される。

<img style="width: 1050px;" src="https://test.com/img/xxxxx.webp">

これをレスポンシブ対応でmax-width:100%にしたい等、自身でstyleを指定したいような場合は以下で出力すると良い。

$(selector).summernote('pasteHTML', `<img src="${result.url}" style="max-width: 100%;">`);

 - PHP jQuery JavaScript

  関連記事

jQueryのプラグインで簡単にイメージスライダーを作成(bxslider)
jQueryのプラグインで簡単にイメージスライダーを作成(bxslider)

画像のスライダーを作る時に便利なプラグインであるbxslider。簡単に使えるし ...

jQueryで画像(<img>タグ内)のsrc情報を取得する場合
jQueryで画像(タグ内)のsrc情報を取得する場合

すぐに出てこなかったのでメモ。 $('#test').attr('src'); ...

javascriptやjQueryで画像を書き換えても変わらない場合
javascriptやjQueryで画像を書き換えても変わらない場合

けっこう前に画像をアップロードしてリアルタイムにプレビューする機能を作った時に、 ...

ライブラリ等を使用せずファイルをドラッグ&ドロップでアップロードする方法
ライブラリ等を使用せずファイルをドラッグ&ドロップでアップロードする方法

formにドラッグ&ドロップでファイルをアップロードする機能を実装する場 ...

Ajaxでプラグイン無しのファイルアップロード方法
Ajaxでプラグイン無しのファイルアップロード方法

画像ファイルを添付できて、尚且つAjaxを用いた非同期通信のフォームを作りたかっ ...