最近一直在完善mongoose做webserver的項目,其中程序升級要通過前端傳輸升級包到服務器。
因為第一次寫前端代碼,分片傳輸的邏輯,網上一堆,大同小異,而且版本啊,API不一致的問題,導致頭疼的很。后面廢了九牛二虎之力,想出了用websocket傳輸二進制升級包,再另加一個消息發送文件名,也就是二進制文件和文本文件分兩次發送。
function sendFile(file) {const reader = new FileReader();reader.onload = function(event) {const arrayBuffer = event.target.result;const uint8Array = new Uint8Array(arrayBuffer);const chunkSize = 10240;for (let i = 0; i < uint8Array.length; i += chunkSize) {const chunk = uint8Array.slice(i, i + chunkSize);websocket.send(chunk);}};reader.readAsArrayBuffer(file);
}
今天騰出時間來了,看著這個明顯不合理的做法。在網上查了下,同時傳輸二進制和文本消息的可以用http協議中的multipart/form-data。
# 請求頭 - 這個是必須的,需要指定Content-Type為multipart/form-data,指定唯一邊界值
Content-Type: multipart/form-data; boundary=${Boundary}# 請求體
--${Boundary}
Content-Disposition: form-data; name="name of file"
Content-Type: application/octet-streambytes of file
--${Boundary}
Content-Disposition: form-data; name="name of pdf"; filename="pdf-file.pdf"
Content-Type: application/octet-streambytes of pdf file
--${Boundary}
Content-Disposition: form-data; name="key"
Content-Type: text/plain;charset=UTF-8text encoded in UTF-8
--${Boundary}--
結果發送小的文本文件貌似沒啥問題,發送100多M的大文件,就死活搞不成功了,關鍵的是不知道咋解析帶有boundary的body內容,有個mg_http_next_multipart貌似沾點邊,但是沒能解析成功。
只好繼續百度了,結果發現好像mg_http_upload也有點沾邊,但是這個函數的含義,具體用法,又沒有注釋,真是難啊。
突然想著可以去微軟的bing搜索一下,立馬就出來了mongoose的官網,里面就寫了如何傳輸需要分片大文件的過程,具體參考mongoose手冊。
哎,真是多少年了,還是犯以前在學校的錯誤。課本概念,例子不弄清楚,就到處去看輔導書。這會是有現成的官網例子不看,成天去看別人的二手資料,也不知道資料是基于哪個API版本。
既然如此,那就下載mongoose手冊吧,這下可好了,github又打不開了,真是想干點事,太難了。
那就去gitee吧,一搜,還真有其他好人,早就把mongoose搬過來了,下載簡直不要太舒服了。
一切就緒,現用例子試試分片的例子。
document.getElementById('fileMcu').addEventListener('change', function(event) {selectFile = event.target.files[0];var r = new FileReader();r.readAsArrayBuffer(selectFile);r.onload = function() {sendFileData(selectFile.name, new Uint8Array(r.result), 1024*1024);};});var sendFileData = function(name, data, chunkSize) {var sendChunk = function(offset) {var chunk = data.subarray(offset, offset + chunkSize) || '';var opts = {method: 'POST', body: chunk};var url = '/uploadMcu?offset=' + offset + '&file=' + encodeURIComponent(name);fetch(url, opts).then(function(res) {if (res.ok && chunk.length > 0) sendChunk(offset + chunk.length);return res.text();});};sendChunk(0);};
后端C++代碼接收文件時,只用一句話,
mg_http_upload(connection, httpReq, &mg_fs_posix, “.”, 999999999);
簡直不要太舒服了,都不用自己拼包,自己打開文件寫入。
這個時候看了下mg_http_upload的源碼,里面就有獲取 mg_http_get_var(&hm->query, “offset”, buf, sizeof(buf));
mg_http_get_var(&hm->query, “file”, file, sizeof(file));語句。
這個好熟悉啊,不就是上面的URL后面跟的參數嗎?難怪不用自己拼包,打開文件寫入呢,你按照正確的格式傳輸數據,mg_http_upload就能搞定所有的事。
這個周末值了,頭疼的事情搞定了。從這個事情明白了,看官網手冊的重要性,不得不說老外搞開源項目還是很正規的,值得我們學習。還有就是做一件事情難點很多,哪怕你搞定了最重要的80%,還有不起眼的20%照樣能摧毀你,怎么樣讓自己的知識系統化,全面化,這樣才能不斷往前推進。