上一篇文章的Python程式透過MediaFileUpload()建立上傳檔案物件。本文將修改《使用Python Flask建置影像圖檔上傳網站服務(五)》的Flask網站程式,原程式是將用戶透過網頁表單上傳的檔案存入網站伺服器的uploads資料夾,這個版本改成把檔案暫存在主記憶體,再交由MediaIoBaseUpload()建立上傳檔物件,轉傳到Google雲端硬碟。
MediaFileUpload(媒體檔案上傳)類別物件的參數
MediaFileUpload()其實有4個參數(參閱此Google API說明文件),它們的名稱和意義如下:
- filename:檔名,字串格式的上傳檔路徑和檔名。
- mimetype(MIME類型):上傳檔的MIME類型字串,如果沒有填寫,則依「副檔名」自動判斷。
- chunksize:區塊大小,僅resumable(可續傳)參數設為True才有效,指定切割檔案、分批上傳的位元組(整數)大小。Google雲端程式的「請求」訊息大小上限為5MB,所以區塊大小不可大於5MB,預設為1024×1024位元組(1MB)。此參數設成-1代表不分割(不建議)。
- resumable:可續傳,設成True代表可在斷線、重新連線之後續傳檔案。
使用io.BytesIO()建立緩存記憶體物件
Python的標準函式庫內建一個名叫io(代表「輸出∕入介面」)的模組,其中的BytesIO提供了在記憶體中讀寫位元組資料的功能,例如,把二進位檔案資料(如:圖檔)暫存在記憶體。
延續《使用Python Flask建置影像圖檔上傳網站服務(五)》貼文的Flask程式,上傳檔案的資料存在file物件,底下的敘述將建立一個BytesIO物件,然後在其中存入上傳檔案。程式碼的開頭必須先引用io模組:
Flask網站應用程式的src原始檔資料夾,也要存入Google的服務帳戶金鑰。
透過MediaIoBaseUpload物件上傳緩存記憶體內容到Google雲端硬碟
完整的上傳檔案到Google雲端硬碟的Python Flask主程式碼如下,這個程式也支援把ESP32-CAM拍攝的影像上傳到的Google雲端硬碟,ESP32-CAM的程式碼不變,請參閱《ESP32-CAM開發板(三):拍照並上傳影像到網站伺服器》貼文。
import datetime import filetype from flask import Flask, flash, request, redirect, url_for, render_template from google.oauth2 import service_account from googleapiclient.discovery import build from googleapiclient.http import MediaIoBaseUpload import io # 內含BytesIO類別 UPLOAD_FOLDER = '你的上傳檔資料夾的ID ' SCOPES = ['https://www.googleapis.com/auth/drive'] SERVICE_ACCOUNT_FILE = 'google_auth.json' # 金鑰檔案 ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} 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 # 驗證憑證 creds = service_account.Credentials.from_service_account_file( SERVICE_ACCOUNT_FILE, scopes=SCOPES) service = build('drive', 'v3', credentials=creds) @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']) # 供ESP32-CAM上傳影像的路由 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) # 判斷上傳檔的類型 MIME = file.content_type if file_type in ALLOWED_EXTENSIONS: file.stream.seek(0) # 重新設定檔名 filename = str(datetime.datetime.now()).replace( ':', '_') + '.' + file_type buffer = io.BytesIO() # 宣告記憶體物件 file.save(buffer) # 把檔案存入緩存記憶體 print("上傳到Google Drive...") media = MediaIoBaseUpload(buffer, mimetype=MIME, chunksize=1024*1024, resumable=True) body = {'name': filename, 'parents': [UPLOAD_FOLDER]} file_id = service.files().create( body=body, media_body=media).execute() print('雲端檔名: ' + str(body['name'])) print('雲端檔案ID:' + str(file_id['id'])) # return {"msg": 'ok', "filename": filename} # 改成傳遞Google雲端檔案的ID給HTML樣板 return {"msg": 'ok', "filename": str(file_id['id'])} else: return {"msg": 'type_error'} # 傳回代表「檔案類型錯誤」的訊息 if __name__ == "__main__": app.run(host='0.0.0.0', port=80)
顯示上傳到Google雲端硬碟的影像檔
Flask程式上傳圖檔之後,網頁將會呈現剛剛上傳的影像,原本的HTML樣板檔(templates/index.html)是讀取伺服器的static/uploads路徑的上傳影像,要改成讀取Google雲端硬碟的上傳檔,其中的filename變數值是Flask傳入的Google雲端檔案ID。
完整的Python Flask上傳影像到Google雲端硬碟的程式原始碼請按此連結下載。