Upload file 是一個比較特別的 Component,如果我們想直接利用既有的 rerun 功能來傳送檔案資料,就必須先將檔案轉換成 base64
編碼,再將整個編碼後的資料一併傳給伺服器。然而,這種做法會帶來兩個問題:
base64
後,資料量會變得更大,增加了網路傳輸的負擔。base64
轉換會消耗更多的計算資源,影響用戶體驗。為了避免上述問題,我們為檔案上傳新增一個 endpoint 。前端在選擇檔案後,直接將檔案透過這個 endpoint 傳送到伺服器。伺服器處理完檔案後,再觸發 rerun 操作。
前端:
async function uploadFile(compID, file) {
// ...
const formData = new FormData()
formData.append('file', file, file.name)
// ...
}
function createUploadFile(comp) {
// DOM
inputEle.onchange = (e) => {
e.preventDefault();
if (!e.target.files) {
return
}
const file = e.target.files[0]
uploadFile(e.target.id, file).then(async resp => {
if (resp.status != 200) {
console.error(resp)
return
}
const fileResp = await resp.json()
state[e.target.id] = fileResp
rerun({})
})
}
}
後端:
http.HandleFunc("POST /api/files", func(w http.ResponseWriter, r *http.Request) {
// ...
err := r.ParseMultipartForm(MaxUploadSize)
// ...
file, handler, err := r.FormFile("file")
// ...
// 這裡我直接把檔案塞給記憶體了,但理論上需要開選項給開發者選擇記憶體或暫存檔案。
bs, err := io.ReadAll(file)
//...
stateFile := &tgstate.File{
Name: handler.Filename,
Size: handler.Size,
Type: http.DetectContentType(bs),
Bytes: bs,
}
state.Set(compID, stateFile)
// ...
})
目前的實作有幾個問題:
這些坑先暫時記著好了,之後再回來填。