Go 語言在 Linux IoT 開發板上處理大檔案上傳的解決方法
近年來不少本來用於物聯網開發的主板已經差不多具備 10 年前電腦主機的 IO 速度和處理水平了。在網上不難看到具備 1000Mbps 網絡接口並同時使用多核心的處理器的單片電腦(SBC),價格上也不太貴(約 100 - 120HKD 一塊),可玩性還是很高的。 其中我使用過的兩種開發板:ZeroPi 跟 OrangePi Zero Plus 然後,我想用它來組網絡儲存系統 對,理論上你是可以用這種 SBC 來做網絡儲存器(NAS),我們一個一個來看: ✔️ CPU: H3 / H5 處理器 ( 1.3Ghz ,一般作為文件伺服器完全沒問題) ✔️ 1000Mbps Ethernet / 網絡接口 ✔️ USB2.0 接口(480Mbps,對於家中只有 100Mbps 上下載的我來說完足夠) ✔️ 40 x 42 cm,可以直接黏在硬碟盒後面 ✔️ 運行時只需要 5V 0.2 - 0.3A,超級省電 可是問題就是它只有 512MB 的 RAM 啊!!! 現在這年代連最便宜的 Synology DS118 都要比它多 512MB RAM 啊! 可是,要這麼多 RAM 到底用來幹麼? Golang 網頁伺服器的文件上載的運作原理 一般來說使用 Golang 處理文件上載的邏輯都長這樣 r.ParseMultipartForm(10 << 20) file, handler, err := r.FormFile("myFile") if err != nil { fmt.Println("Error Retrieving the File") fmt.Println(err) return } defer file.Close() //然後用 io.Copy 把暫存檔案複制到目的地,這裡我就不仔細寫了 在一般電腦上運行這段代碼是完全 OK 的,在上載檔案時 Golang 會把 multipart form data 中的檔案緩存到 RAM,然後在 io.Copy 之後,defer 的 file.Close() 會把 Reader 關掉,之後交由 GC 處理刪除。 這情況只能適用於上載的檔案比系統能用的 RAM 還要細的情況,對於像 ZeroPi / Orange Pi 這種 SBC 要上載大型檔案,就不能用這傳統的方法,而需要特別處理了。不然就會出現以下情況 就是所謂的「爆 RAM」 那讓我們先來了解一下 parseMultipartForm 的原理吧,簡單來說是 你的 browser 把檔案塞到了一個 HTTP FORM 裡面browser 把 FORM 的資料連同你要的檔案在同一個(或多個) Request 裡面送到伺服器因為檔案太大,沒有辦法使用同一個 HTTP REQUEST 處理,所以到達的時候檔案可能被切成幾個 「區塊」,而這個就是 Multipart FormGolang 把這些「 區塊 」寫到 RAM 裡面(在 Debian 的情況下就是寫到 /tmp 裡面)然後如果檔案大小比RAM 大,/tmp 就會被塞爆,出現「no space left on device」錯誤如果 Golang 繼續把東西塞進去,它就會被作業系統殺掉(Killed) 要解決這個問題,我們可以使用兩個方法,首先是不要使用 Golang 內置的 Request library,自己重新寫一個。但是那樣實在太麻煩了,所以我決定直接在前端作更改 WebSocket 是個好東西 說到 WebSocket 很多人會想起類似 Chatroom 或是 Web 的 online game 之類,但是…