本文將改寫Python Flask網站伺服器檔案上傳程式,設置一個新路由“/esp32cam”,接收ESP32CAM微電腦控制板上傳的影像檔,並且將影像檔用上傳的日期與時間重新命名儲存。

處理上傳檔案的程式碼,之前是寫在首頁’/路由的’upload_file()函式,像這樣:

新增處理ESP32CAM控制板上傳影像的程式,跟之前處理網頁上傳檔案的程式,基本上是一樣的,所以筆者把處理上傳檔案的程式碼放在另一個“handle_file”自訂函式。
handle_file()函式接收一個request(請求)物件,然後依照上傳檔的處理情況,傳回「字典」型態的資料,其中的“msg”包含狀態訊息,若上傳檔案處理成功,msg成員的值將是“ok”,而filename成員的值將是上傳檔的名字。
改成呼叫handle_file()函式處理上傳檔的“/”路由程式碼如下:

用當前的日期時間設定上傳檔名
為了避免檔名重複,handle_file()自訂函式會將每個上傳檔的名字改成當前的日期和時間。datetime模組的datetime.now()函式可傳回當前的日期與時間,透過str()函式將它轉成字串;因為Windows系統的檔名不可包含“:”字元,因此最後透過字串物件的replace()方法,把字串裡的所有“:”都替換成“_”。

處理上傳檔案的handle_file函式
底下是handle_file()函式的程式碼,唯有檔案上傳成功,它才會傳回檔名。
def handle_file(request):
if 'filename' not in request.files:
return {"msg": 'no_file'} # 傳回代表「沒有檔案」的訊息
file = request.files['filename'] # 取得上傳檔
if file.filename == '':
return {"msg": 'empty'} # 傳回代表「空白」的訊息
if file:
file_type = filetype.guess_extension(file) # 判斷上傳檔的類型
if file_type in ALLOWED_EXTENSIONS:
file.stream.seek(0)
# filename = secure_filename(file.filename)
# 重新設定檔名:日期時間 + ‘.’ + ‘副檔名’
filename = str(datetime.datetime.now()).replace(
':', '_') + '.' + file_type
file.save(os.path.join(
app.config['UPLOAD_FOLDER'], filename))
# 傳回代表上傳成功的訊息以及檔名。
return {"msg": 'ok', "filename": filename}
else:
return {"msg": 'type_error'} # 傳回代表「檔案類型錯誤」的訊息
處理ESP32CAM控制板上傳檔案的路由
處理ESP32CAM上傳檔案的“/esp32cam”路由程式,只需把連線請求轉給handle_file()函式,再把函式傳回的訊息發送給用戶端(ESP32CAM開發板)。

修改之後的完整Python Flask網站伺服器程式碼(app.py檔)如下:
import datetime
import os
import pathlib
import filetype
from flask import Flask, flash, request, redirect, url_for, render_template
from werkzeug.utils import secure_filename
# 取得目前檔案所在的資料夾
SRC_PATH = pathlib.Path(__file__).parent.absolute()
UPLOAD_FOLDER = os.path.join(SRC_PATH, 'static', 'uploads')
app = Flask(__name__)
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' # 請自行修改密鑰
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 3 * 1024 * 1024
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
@app.route('/')
def index():
return render_template('index.html')
@app.route('/', methods=['POST'])
def upload_file():
res = handle_file(request)
if res['msg'] == 'ok':
flash('影像上傳完畢!')
return render_template('index.html', filename=res['filename'])
elif res['msg'] == 'type_error':
flash('僅允許上傳png, jpg, jpeg和gif影像檔')
elif res['msg'] == 'empty':
flash('請選擇要上傳的影像')
elif res['msg'] == 'no_file':
flash('沒有上傳檔案')
return redirect(url_for('index')) # 令瀏覽器跳回首頁
@app.route('/esp32cam', methods=['POST'])
def esp32cam():
res = handle_file(request)
return res['msg']
def handle_file(request):
if 'filename' not in request.files:
return {"msg": 'no_file'} # 傳回代表「沒有檔案」的訊息
file = request.files['filename'] # 取得上傳檔
if file.filename == '':
return {"msg": 'empty'} # 傳回代表「空白」的訊息
if file:
file_type = filetype.guess_extension(file) # 判斷上傳檔的類型
if file_type in ALLOWED_EXTENSIONS:
file.stream.seek(0)
# filename = secure_filename(file.filename)
# 重新設定檔名:日期時間 + ‘.’ + ‘副檔名’
filename = str(datetime.datetime.now()).replace(
':', '_') + '.' + file_type
file.save(os.path.join(
app.config['UPLOAD_FOLDER'], filename))
# 傳回代表上傳成功的訊息以及檔名。
return {"msg": 'ok', "filename": filename}
else:
return {"msg": 'type_error'} # 傳回代表「檔案類型錯誤」的訊息
@app.route('/img/<filename>')
def display_image(filename):
return redirect(url_for('static', filename='uploads/' + filename))
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)

您好,想請教我應該如何讓ESP32-CAM拍照後將照片上傳至FLASK,並請FLASK上傳雲端辨識訓練模型,並回傳訊息至資料庫(先不管資料庫這一段),我目前遇到無法讓ESP32去連結flask,參考您的程式,__file__,這個我也無法定義,應該怎麼做比較好?
這是一個HTTP前端(ESP32)+ 後端(Flask)的應用,建議分開除錯,例如,編寫一個透過HTML表單上傳影像的Flask伺服器程式,確認OK,再測試ESP32的上傳影像程式是否正常。
遇到ESP32連結在GCP上的FLASK,ESP32一直產生502得回覆,請問該怎麼解決?