deta.sh - URLを指定しないで画像を表示する
deta.shでMicroを使うとき、テンポラリーファイルを作ろうとするとエラーになってしまいます。マニュアルをみると/tmpにしかデータが作れない、read only systemとなっています。
となると、imgのsrcでurlを指定して画像表示ができないことになります。/tmpを汚しては申し訳ないので、なんとかならないか試行錯誤が始まりました
生徒発案でBase64コードをそのまま表示する方法がわかる
一緒に授業をしていた生徒もdeta.shを実験していたのですが、やはり同じところで躓いてある方法を発見しました。
画像データをbase64に変換してデータベースに保存しておき、それを画面に表示する方法です
普通は srcにurlを指定する <img src="abc.jpg" alt=""> urlが使えないのでbase64に変換して、そのコードをそのまま入れる <img src="data:image/jpeg;base64,sdfg8dppga0u...dfhgh" alt="">
ただこの方法だとデータベースが大きくなるのでDriveの画像ファイルをその場で変換してbase64で表示するようにしてみました。
Driveの画像ファイルを表示するサンプル
DriveにuploadしたDriveのファイルを名前指定で表示するサンプルを作ってみましょう。
htmlは画像ファイル名を入れるindex.htmlと表示するdownload.htmlそして、それをコントロールするmain.pyで作成します。いつものようにbootstrapを使ってます
新規Microを作成する deta new --python --name drivegettest フォルダーに以下のファイルを作成していきます main.py requirements.txt \templates index.html download.html
index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>formTest</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> </head> <body> <h1 class="display-1 text-primary">deta.sh Driveからのダウンロード</h1> <div class="m-5 border"> <p>Drive(photos)に保存したファイル名を入力してください</p> <form action="/download" method="post"> <div class="mb-3 row"> <div class="col-sm-4"> <input type="text" name="file" size="20" maxlength="200"> </div> </div> <div class="mb-3 row"> <div class="col-sm-10"> <input class="btn btn-primary" type="submit" value="DOWNLOAD"> </div> </div> </form> </div> <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous"></script> </body> </html>
index.htmlでformを作り、main.pyにPOSTしています。name="file"で送っているので、mainでそれを受信して操作します
download.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>formTest</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> </head> <body> <h1 class="display-1 text-primary">deta.sh Driveからのダウンロード</h1> <div class="m-5 border"> <p>Drive(photos)に保存したファイルを表示します</p> <p class="display-5 text-secondary">filename: {{filename}}</p> <img src="data:image/jpeg;base64,{{image | safe }}" alt=""> </div> <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous"></script> </body> </html>
index.htmlから渡されたファイル名をmain.pyでDriveから読み込み、base64に変換してdownload.htmlに渡しています。imgのsrcにbase64で記述した場所にjinja2でbase64のコードをそのまま展開して画面表示します。注意点はあまり大きな画像ができないこと。理由はどうやらタイムアウトらしく各レスポンスが10秒以内でないといけません。
MicroのDetailsをみるとたしかに Timeout: 10sと書いてあります。
requirements.txt
flask deta
flaskとdetaを使用しています
main.py
from flask import Flask, render_template, request, Response from deta import Deta import base64 app = Flask(__name__) #drive接続 deta = Deta("Project key") photos = deta.Drive("photos") @app.route("/", methods=["GET"]) def top(): return render_template("index.html") @app.route('/download',methods=['POST']) def download_img(): filename = request.form['file'] large_file = photos.get( filename ) image_data = b'' for chunk in large_file.iter_chunks(4096): image_data += bytearray(chunk) large_file.close() img = base64.b64encode(image_data).decode('utf-8') return render_template("download.html",image=img,filename=filename) if __name__ == '__main__': app.debug = True app.run(host='localhost')
最初の呼び出しは top()で処理し、index.htmlを呼び出しているだけです
/downloadへのpostを download_img()で処理します。渡されたfile名で Driveの"photos"のオブジェクトをlarge_fileに受けます。
このオブジェクトはファイルとのパイプのようなもので、ここから少しづつファイルを受信する、chunkという方法です。 4096バイトづつ受信してはimage_dataにbytearrayで保存します。そのデータをbase64に変換して、utf-8で文字列にしてdownload.htmlに送ります
サンプルをdeta.shに作りましたので、公開します。
deta.shに公開したdrivegettestsample.jpgとsample2.jpgが表示可能です
これでdeta.shでプログラムを作る準備ができましたので、もう少し大きめのサンプルを作っていこうと思います