ブラウザ上にドラッグ&ドロップした画像ファイルを、Base64形式に変換してtextareaにセットするのと、画像を表示するのを両方やってみます。
はじめに
前回、手描きの図を画像フォーマットに変換してBase64形式でrequestにのせるケースをやりました。
今回はその続きです。
前回も書きましたが、Python等で分類器などを作ってWEBブラウザ上でデモなどをするとき、必要になるのが「画像イメージをWEBブラウザ上で取得して、SUBMITでPython等サーバー側の処理に渡せる形式(Base64)に変換する」という処理です。
それを散発的に記事にしていたのですが、自分でもど忘れした時に、あちらこちら見るのが面倒になってきたので、ちょこっとまとめておこうと思ってやる2回目です
今回は、 WEB画面にドラッグ&ドロップした画像ファイルを取り込むパターンでやってみます。
Django2.0+bootstrap4のベース(jQuery付き)です。
HTML部分から
Django2.0のテンプレート機能を使い、Base.htmlを読み込んでます。
Base.htmlの内容等は以下の記事のやり方を踏襲しています。
埋め込む部分の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
原型は、こちらの記事のものです。
その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("ドロップできるのは、JPEG・PNG画像ファイルだけです"); } }); $(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("ドロップできるのは、JPEG・PNG画像ファイルだけです"); }
まず。
ドロップされたファイルを受け取ります。
var file = e.originalEvent.dataTransfer.files[0];
あとは、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/」のようにアクセスします。
ここに、適当な画像ファイルをドロップしてみます。
で。
とりあえず、サブミットで引き継がれることだけ確認しておきます。
OKですね。
今回はこんなところで。
ではでは。