"BOKU"のITな日常

還暦越えの文系システムエンジニアの”BOKU”は新しいことが大好きです。

WEBブラウザ上にドラッグ&ドロップした画像ファイルをBase64に変換して表示する

 ブラウザ上にドラッグ&ドロップした画像ファイルを、Base64形式に変換してtextareaにセットするのと、画像を表示するのを両方やってみます。

f:id:arakan_no_boku:20191216111232p:plain

 

はじめに

 

前回、手描きの図を画像フォーマットに変換してBase64形式でrequestにのせるケースをやりました。

arakan-pgm-ai.hatenablog.com

今回はその続きです。

前回も書きましたが、Python等で分類器などを作ってWEBブラウザ上でデモなどをするとき、必要になるのが「画像イメージをWEBブラウザ上で取得して、SUBMITでPython等サーバー側の処理に渡せる形式(Base64)に変換する」という処理です。

それを散発的に記事にしていたのですが、自分でもど忘れした時に、あちらこちら見るのが面倒になってきたので、ちょこっとまとめておこうと思ってやる2回目です

今回は、 WEB画面にドラッグ&ドロップした画像ファイルを取り込むパターンでやってみます。

Django2.0+bootstrap4のベース(jQuery付き)です。

 

HTML部分から

 

Django2.0のテンプレート機能を使い、Base.htmlを読み込んでます。

Base.htmlの内容等は以下の記事のやり方を踏襲しています。

arakan-pgm-ai.hatenablog.com

埋め込む部分のHTMLです。

{% extends 'base.html' %}
{% load static %}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}
{% load widget_tweaks %}

{% block header %}
<link rel="stylesheet" href="{% static "css/dragdrop.css" %}"></link>
{% endblock %}

{% block title %}
  サンプルその1
{% endblock %}

{% block content %}
<div class="container">
    <form action="" method="post">{% csrf_token %}
        {% if base64text %}
        <h4>ドロップされたファイル名</h4>
        <div class="form-group row my-4">
            <img  src="{{ base64text }}" />
        </div>
        <a href="{% url 'dragdrop' %}"><h2>もう一回</h2></a>
        {% else %}
        <h4>ドラッグエリア</h4>
        <div class="form-group row my-4">  
        	<div id="dragandrophandler" class="border col-lg-9">ここにJPEGまたはPNGファイルをドロップ</div>
        </div>
        <div class="form-group row my-4">         
    		<div class="col-lg-2">       
	            <button type="submit" class="btn btn-primary">サブミット</button>
    		</div>
    		<div class="col-lg-8"></div>       
        </div>
        <div id="imgdiv" class="form-group row my-4"> 
            <img  src="" />
        </div>
        <div class="form-group row my-4" hidden>         
            {{form.areaone|add_class:"form-control"}}
    	</div>    
        {% endif %} 
    </form>
</div>  
<script type="text/javascript" src="{% static "js/dragdrop.js" %}"></script>  
{% endblock %}

補足します。

JavaScript「dragdrop.js」内で、ID固定で使用している箇所が3か所あります。

最初の2つは。

ドロップエリア

<div id="dragandrophandler" class="border col-lg-9">ここにJPEGまたはPNGファイルをドロップ</div>

 ドロップした画像を表示するエリア

<div id="imgdiv" class="form-group row my-4">
    <img src="" />
</div>

です。

あと、3つめは、少々わかりづらいのですが、サブミットした時に、requestにのせてPython側の処理に受け渡すために、画像ファイルをBase64形式テキストにしてセットするtextareaを定義していて、これもIDを固定で使っています。

このフォームは「forms.py」で以下のように定義されています。

こちらでIDを指定しているわけです。

forms.py

from django import forms


class UserForm(forms.Form):
    areaone = forms.CharField(
        label='',
        widget=forms.Textarea(
            attrs={
                'id': 'resultbase64',
                "rows": 20,
                "cols": 10,
            }
        )
    )

ここは見える必要がないので、<div>タグをhiddenにしています。

 

ドラッグ&ドロップで画像を表示するJavaScript

 

原型は、こちらの記事のものです。

arakan-pgm-ai.hatenablog.com

そのDropイベントに対応する部分だけを修正しています。

dragdrop.jsです。

var obj = $("#dragandrophandler");
obj.on('dragenter', function (e) 
{
    e.stopPropagation();
    e.preventDefault();
    $(this).css('border', '2px solid #0B85A1');

});
obj.on('dragover', function (e) 
{
     e.stopPropagation();
     e.preventDefault();
     $("#dragandrophandler").css('background-color', '#ffffe0');
});
obj.on('drop', function (e) 
{
     $(this).css('border', '2px dotted #0B85A1');
     e.preventDefault();
     var file = e.originalEvent.dataTransfer.files[0];
    if(file.type === 'image/jpeg' || file.type === 'image/png') {
         //We need to send dropped files to Server
        const reader = new FileReader()
        reader.onload = () => {
            $("#imgdiv").children('img').attr('src', reader.result);
            $("#resultbase64").val(reader.result);
        }
        reader.readAsDataURL(file);    
        $("#dragandrophandler").css('background-color', '#ffffff');
    } else {
        alert("ドロップできるのは、JPEGPNG画像ファイルだけです");
    }

});
$(document).on('dragenter', function (e) 
{
    e.stopPropagation();
    e.preventDefault();
    $("#dragandrophandler").css('background-color', '#ffffff');

});
$(document).on('dragover', function (e) 
{
  e.stopPropagation();
  e.preventDefault();
  obj.css('border', '2px dotted #0B85A1');
});
$(document).on('drop', function (e) 
{
    e.stopPropagation();
    e.preventDefault();
});

変更した部分だけを補足します。 

     var file = e.originalEvent.dataTransfer.files[0];
     if(file.type === 'image/jpeg' || file.type === 'image/png') {
         //We need to send dropped files to Server
        const reader = new FileReader()
        reader.onload = () => {
            $("#imgdiv").children('img').attr('src', reader.result);
            $("#resultbase64").val(reader.result);
        }
        reader.readAsDataURL(file);    
        $("#dragandrophandler").css('background-color', '#ffffff');
    } else {
        alert("ドロップできるのは、JPEGPNG画像ファイルだけです");
    }

まず。

ドロップされたファイルを受け取ります。

var file = e.originalEvent.dataTransfer.files[0];

JPEGPNGファイルだけを受け付けます。

if(file.type === 'image/jpeg' || file.type === 'image/png')

あとは、FileReaderで画像ファイルを読み込み、「 reader.readAsDataURL(file);」で、Base64形式に変換するわけですが・・。

ここが少しややこしいです。

readAsDataURL(file);は非同期に実行されます。

単純に戻り値で結果を受け取ることはできません。

なので、「reader.onload = () => {」のようにonloadイベントハンドラを定義して、その中でreader.resultにセットされた結果を受け取る必要があります。

reader.readAsDataURL(file);を実行すると、非同期に読み込み処理が動き、終了すると loadend イベントが生じて、同時に result プロパティにBase64エンコードしたデータがセットされるというわけです。

その結果は、reader.resultでとりだせますが、onloadイベントハンドラの外では参照できないので、以下のようにしているわけです。

reader.onload = () => {
    $("#imgdiv").children('img').attr('src', reader.result);
    $("#resultbase64").val(reader.result);
}

 

Djangoのviews.pyとurls.pyを設定

 

動かすために、django側の処理を追加します。

views.py

from django.shortcuts import render
from . import forms
from django.template.context_processors import csrf


def dropdemo(request):
    if request.method == 'POST':
        c = {
            'base64text': request.POST["areaone"],
        }
    else:
        form = forms.UserForm(label_suffix=':')
        c = {'form': form}
        c.update(csrf(request))
    return render(request, 'dragdrop.html', c)

単純にFormを使えるようにしているのと、 サブミットで遷移してきたときに、requestからtextareaフォームにセットしたBase64エンコードテキストを受け取って、HTML側で参照できるように'base64text'にセットしているだけです。

urls.pyには以下を追加します。

path('demo01/', views.dropdemo, name='dragdrop'),

 

さて実行してみます

 

 プロジェクトフォルダ(manage.pyがあるフォルダ)をカレントにして。

python manage.py runserver

 それで、例えば「http://localhost:8000/demo01/」のようにアクセスします。

f:id:arakan_no_boku:20191217222015p:plain

ここに、適当な画像ファイルをドロップしてみます。

f:id:arakan_no_boku:20191217222451p:plain

で。

とりあえず、サブミットで引き継がれることだけ確認しておきます。

f:id:arakan_no_boku:20191217222722p:plain

OKですね。

今回はこんなところで。

ではでは。