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でプログラムを作る準備ができましたので、もう少し大きめのサンプルを作っていこうと思います