Raspberry Pi 的 SD 卡死掉了?有試過 fdisk 嗎?
我因為手頭上有很多台 Raspberry Pi,有時候不小心無故斷電了,Raspberry Pi Boot 不起來而且 SD 卡又寫不進,總會覺得是不是 SD 卡壞掉了? 其實不然,很多時候都只是 Partition Table 出了錯誤,雖然在 Windows 上會不停彈出 "無法格式化" 的錯誤警告,又或者甚至連 Disk Manager 都無法改更磁碟編號,但是卡本身還是可能有救的,最簡單的方法就是拿一台能正常運作的 Raspberry Pi 來跑 fdisk 了 檢查卡本身有沒有物理故障 要檢查這個很簡單,把 SD 卡使用 SD 轉 USB 轉接器插進 Raspberry Pi 的 USB 口前,執行一次下面的指令,插進了後再執行一次,看看有哪個新的儲存裝置彈出來了 ls /dev | grep sd 原本的儲存裝置列表 新的裝置列表,可見 /dev/sdf 出現了 如果你看到 /dev/sdx 出現了的話,恭喜你,你的卡沒壞。 重設 Parition Table 要重設 partition table,你就會需要用到 fdisk 功能。在這例子中我會使用 /dev/sdf 作為例子,你需要把裝置路徑更改到你的 SD 卡的儲存裝置編號。 首先,使用 sudo 或 su 模式啟動 fdisk sudo fdisk /dev/sdf 然後輸入 p 拍 enter,你會看到現在存在在卡上的 partition。輸入 d + enter ,重複直至所有 partition 被刪除。 輸入 n + enter 創建一個新的 partition,其餘的問題全部用預設值就好 之後再按 p + Enter,你會看到新的 Linux partition 已經準備好了。這個時候,你要把 Partition Type 設成 Windows 能讀取的格式,輸入 t + enter 之後輸入 L + enter,找到 Microsoft Basic Data 的代號 (在下面的例子是 11 號),輸入 11 + enter。 找到 Microsoft Basic data 的 partition type 完成之後你的 SD 卡 Partition table 應該會長這樣 看到 Sd 卡容量跟 Type = Microsoft Basic Data 最後就是輸入 w + enter 把更改真正的寫進 SD 卡內就大功告成了! Windows 格式化 把 SD 卡重新插到 Windows 主機上, Windows 會彈出提示要求把 SD 卡進行格式化。你最後只需要按照一般 Windows 格式化的方法就可以把 SD 卡救回來了 最後被救回的 SD 卡
由網頁拖放檔案到桌面的神奇方法
你有沒有想過到底怎樣可以把檔案由 DOM ELEMENT 拖放下載到桌面? 由本地的檔案管理員或是桌面拖放檔案到網頁上的話大家應該都很容易在一些網站如 facebook 或 youtube 看到可是說由網頁端拖放檔案到桌面就更少見了。 為甚麼我從來都沒看過有網站用這個方法讓使用者下載檔案? 這當然是有原因的,因為這個功能只有 Google Chrome 支援而已。對於不能跨平台用的 API 沒人用那當然很正常吧?但是作為研究最新技術的 imuslab,這個有趣的功能當然要加進去 ArOZ Online 系統吧?但是,要使用這個檔案由網頁拖放到桌面卻是有這麼堆特別要求: 拖放的元件必須是連接 (<a>)元件的 href 一定要填寫不能留空,但是也可以填 javascript:void(0); draggable 需要設為 true不能使用DOM 屬性 ondragstart ,需要使用 JavaScript 來 加 EventListener 如果你的情況符合上面的條件,你就可以把檔案透過這個 <a> Element 拖放到本地檔案系統了。以下為一個簡單的 Example: $("#element")[0].addEventListener("dragstart",function(evt){ var targetMime = "plain/text"; $.ajax({ url: "media/getMime/?file=" + encodeURIComponent(properties.filepath), success: function(data){ if (data.error !== undefined){ targetMime = "text/directory"; }else{ targetMime = data; } }, async: false }); evt.dataTransfer.setData("DownloadURL",targetMime+":"+ properties.filename +":"+ "http://" + location.hostname + ":" + location.port + "/media/?file=" + encodeURIComponent(properties.filepath)); },true); 到這裡有幾點值得注意 MIME Type 必須符合檔案的內容,不然拖放之後會出現 Internet Shortcut 檔案而不是檔案本身如果要在 event 裡使用 AJAX,那 Request 必須為 Synchronize ,就好像上面的例子中用來要求取得檔案 MIME Type 的 AJAX request 一樣當設定 DownloadURL 參數時時必需使用完整 URL 而不能使用 Relative PathaddEventListener 的 Option 值必須為 true 就是這樣,你就能成功的做出一個可以拖放到桌面的 HTML5 DOM Element 了喔! 19/6/2020 補充 如果你在下載的時候遇到這個問題 失敗 已封鎖的例子 一般是因為以下兩個原因 你寫入的 URL 不正確或檔案不存在上面的例子裡的一個小 Bug (Hard code 了 http 的問題) 對於非 http 的使用者來說,你可以把這一行 evt.dataTransfer.setData("DownloadURL",targetMime+":"+ properties.filename +":"+ "http://" + location.hostname + ":" + location.port + "/media/?file=" + encodeURIComponent(properties.filepath)); 改成 evt.dataTransfer.setData("DownloadURL",targetMime+":"+ properties.filename +":"+ location.protocol + "//" + location.hostname + ":" + location.port + "/media/?file=" + encodeURIComponent(properties.filepath)); (注意多出來的 location.protocol)來自動 Detect 目前是使用 http: 還是 https: 協議
使用 Golang 動態載入插件 / 模組的方法
Golang 跟 PHP 或 Nodejs 不一樣,是一種需要預先 compile 的語言,相信這個大家都知道了。然而,這有一個很大的問題,就是無法像 php 那樣動態載入插件 / 模組。 那如果我想在已經 Compile 好的 Golang 程式上加上額外的功能應該怎樣做? 一開始我就為這問題煩惱了很久,然而最後我發現了一個不錯的解決方法,就是使用 Reverse Proxy。舉個例子,如果你能夠設定動態的 Reverse Proxy ,然後把一個新的模組指向一個新的 subdirectory ,那樣看上去不就跟新增了功能很相近了嗎? 所以,就在新的 ArOZ Online Core 裡面加入了這樣的一個結構,這裡我把它稱為 Subservice: Subservice 的概念很簡單,就是把 Reverse Proxy 的 process 拼進去跟 Core 共用同一個 STDIN 跟 STDOUT,然後網頁的部分就是用 Reverse Proxy 來處理,看上去所有模組都是由同一個程式所執行似的(實際上卻不是),它的運作邏輯大約是這樣: 掃描所有存在的資料夾,看看裡面有沒有可執行檔案使用 cmd.exec 加上 -info argument 啟動,等待回傳 JSON 格式的啟動設定把 working directory 轉換到資料夾內,並以 cmd.exec -port {指派的端口} 執行該程式把程式的 STDERR 跟 STDOUT 都指向 parent 的 STDOUT把服務要求的 Reverse Proxy 終端指向到該程式在用家存取該終端的時候進行 URL rewrite【在 Reverse Proxy 下使用模組】在parent 遇上 SIGKILL 的時候把 SIGKILL 都傳送到子服務並關掉程式 問題:可是這樣就存取不了伺服器的 DB 跟 File System API 對,所以這方法後來改成了只讓非兼容 ArOZ Online 的模組使用(例如 Aria2)。對於真的有需要存取系統核心部分 API 的模組來說,這個方法無法處理到 Database 存取跟 File System Virtualization 的部分,如果把 系統核心 API 都用 RESTful 的方式開出來錄又會有另一些安全問題,所以得想另一個方法去處理這個部分。 欸,那我內嵌一套 JavaScript Interpreter 不就可以了嗎? AJGI 架構 沒錯,所以後來就使用了一個新的架構,暫名 AJGI。這是一個可以讓系統執行 Javascript 的方法,同時讓 JavaScript 存取到系統內部的 function,可謂一舉兩得,至於效率嘛,既然你都是在寫插件就不要介意這麼多了。簡單來說邏輯是這樣的 由 front end 呼叫 AJGI ,並從檔案系統載入一段兼容的 JavaScript 代碼在 JavaScript 虛擬機中執行代碼透過 轉接器 使用 JavaScript 存取 ArOZ Online Core Golang 部分的核心功能(如需要)回傳資料到 frontend結束虛擬機 至於功能和限制之類的就要等之後的開發再進行測試才知道了
threejs 局部縮放變形
目標效果 在 Threejs 中如果你要對一個已經 extrude 的 geometry 進行變型的話,由於缺少內部的功能支援,基本上你就只能在 geometry 上手動的加上一個 Matrix4 變形。對於作我一樣沒學過四次元 Matrix 的軟件工程師來說看到這 4D matrix 就跟看到外星語言一樣。 When in doubt: use Brute Force 作為軟件工程師,當你面對一些無法解決的難題的時候一般只有兩種解決方法: Brute Force Algorithm Neural Network 所以我就理所當然的把所有 Matrix4 裡面有的洞洞都試填一次,最後得出的結論上: 把 Matrix4 第四行第三格填入你想要的縮放比例即可 效果 Matrix4 的結構 原碼大約長這樣: var geometry = new ExtrudeBufferGeometry( shape, data ); geometry.center(); var matrix = new Matrix4(); matrix.set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0.1, 1); geometry.applyMatrix( matrix ); updateGroupGeometry( mesh, geometry ); 所以說真的是 Brute Force 大法好!
Golang Header 中的 UTF-8 檔名
一般來說如果你想讓 Golang 去 Serve 一個 File 你會在檔頭加入以下兩行: w.Header().Set("Content-Disposition", "attachment; filename=" + filepath.Base(realFilepath)) w.Header().Set("Content-Type", r.Header.Get("Content-Type")) 這樣瀏覽器就不會播放多媒體檔案而是轉為下載模式。然而,對於一些含有特別字元 / 空格的檔案名稱,一般瀏覽器並無法完整讀取整個檔名,例如: Chōcho - Authentic Symphony (Acoustic ver.).mp4 //檔案名稱下載時因為空格會被 Firefox 讀取成 Chōcho 而如果要解決這個問題,你可以使用 Golang 的 url.QueryEscape( ) 功能,把檔案名稱先轉換為 URL Safe 的名稱,但是這會出現另一個問題,例如上面的例子經過 QueryEscape 之後會變成這樣 Chōcho+-+Authentic+Symphony+(Acoustic+ver.).mp4 這是因為雖然標準裡面有寫到 URL Encode + 跟 " "(空格)是相等的,但是很多主流瀏覽器對 URL Encode 的解讀只有 %20 而已,所以要解決這問題你就只能夠再把 + 替換成 %20 ,完整的代碼如下: w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''" + strings.ReplaceAll(url.QueryEscape(filepath.Base(realFilepath)),"+","%20")) w.Header().Set("Content-Type", r.Header.Get("Content-Type")) 那你可能會說你的 + 號不就是不見了嗎?嗯,沒錯,可是你有更好的解決方法嗎?
Golang 在 filepath.Glob 裡使用含方括號([ 跟 ])的路徑
由於 Golang 語言限制,filepath.Glob 裡面有一些字元是不能用的,例如說 [ 跟 ] 就是其中之一(還有 * 號,但是大部分作業系統都不會讓你把 * 號當作資料夾名稱,所以在這裡就不作處理了。一般來說,如果你要打開一個包含 [ ] 的路徑, filepath.Glob 會回傳空值。 path := "mymusic[mp3]/*" files, err := filepath.Glob(path); // files 會回傳空值 所以,很多工程師就幹脆不在資料夾名稱裡面使用 [ 跟 ],但是作為開發網頁版的檔案管理器,由於作為工程師是無法預計用戶的使用方式,我們必將要在 Glob 裡面支援 [ 跟 ],而方法可能比你想像中的簡單: realpath := "mymusic[mp3]" files, _ := filepath.Glob(realpath + "*") //這裡的 files 會是空值,寫在這裡是為了在下面省下一個 else 的 case if (strings.Contains(realpath, "[") == true || strings.Contains(realpath, "]") == true){ if (len(files) == 0){ //特別處理模式,以 * 號取代 [ 跟 ] newSearchPath := strings.ReplaceAll(realpath, "[","*") newSearchPath = strings.ReplaceAll(newSearchPath, "]","*") //掃描與輸入路徑相近的路徑名稱 tmpFilelist, _ := filepath.Glob(newSearchPath + "*") //在每個相近路徑中,找出擁有正確路徑的檔案 for _, file := range tmpFilelist{ file = filepath.ToSlash(file) if strings.Contains(file, realpath){ files = append(files, file) } } } } //正確回傳 log.Println(files)
Golang Struct 與 JSON 的快速轉換法
作為前 PHP 開發者,一說到 Golang 要把東西轉做 JSON 就頭痛,因為在 php 裡面你可以非常簡單的把一個複雜的結構轉換成 json,例如說: 就能夠輸出 所以在開發 php 的時候基本上是不用特別處理 json 的轉換的,到了 javascript 一端也能夠輕易的把收回來的資料直接使用,例如: $.get("url_here",function(data){ console.log(data[0]); }); // 輸出 "Hello" 但是去到 Golang 之後寫法就完全不同了,在這篇簡短的文章中,我會簡單介紹一下怎樣輕易的把任何資料 parse 成 JSON string 及由 JSON string 轉回原本型態的方法 Golang 的 Struct 是一樣很神奇的東西 甚麼是 Golang Struct 呢?這個跟 C 語言裡的沒差太遠,簡單來說就是 Golang 上 OOP 的方法。以下是一個簡單的 Struct 例子(原碼取自 ArOZ Online 1.0) type desktopObject struct{ Filepath string; Filename string; Ext string; IsDir bool; IsShortcut bool; IconX int; IconY int; } 把 Struct 轉換成 JSON 這是一個用來定義桌面上的物品的 struct。要把它轉到 JSON 十分簡單,首先你要新建一個 Object thisFileObject := new(desktopObject) 然後把資料填進去,例如這樣 thisFileObject.Filepath = path; thisFileObject.Filename = filepath.Base(path) thisFileObject.Ext = filepath.Ext(path) thisFileObject.IsDir = IsDir(path) 最後把它壓成一個 JSON String jsonString, err:= json.Marshal(desktopFiles); if (err != nil){ //錯誤處理 } return string(jsonString); 就是這樣你的 JSON String 就做好了 由 JSON 轉回 Struct 這個就難一點了,但是還是能做到的。簡單來說就是把 JSON string map 到一個現有的 struct 上面去。先假設我們有以下的一個 struct type iconLocation struct{ X int; Y int; } 和一個 JSON string jsonstring := "你要轉換的東西" 首先你跟上面做的一樣,先預留一個變數給 json unmarshal 時用。然後把 json string 轉換到這個 struct 裡面。 thisLocation := new(iconLocation) json.Unmarshal([]byte(jsonstring ), &thisLocation) fmt.Println(thisLocation) 這樣你就完全把 JSON string 轉回去 struct 了。有夠簡單吧?
WAMP SERVER MySQL 無法連線,因為目標電腦拒絕連線。
最近在幫伺服器由 php 7.0 升級到 php 7.4.4,然而在升級 wordpress 的時候出現了無法連接到資料庫的問題,於是簡單的從網上找到了一個測試資料庫的 php script,發現原來不是 wordpress 的問題,是 MySQL 的問題。 $DBServer = 'localhost'; $DBUser = 'username'; $DBPass = 'password'; $DBName = 'wordpress'; $link= new mysqli($DBServer, $DBUser, $DBPass); if(!$link) echo "失敗!"; else echo "成功!"; 錯誤 解決方法:在 localhost 後面加上 :3307 結果在網上找了一整天也找不到能處理的解決方法,於是在想會不會是 port 設定的問題呢?於是在 localhost 後加上了 :3307 $DBServer = 'localhost:3307'; 然後居然就 fix 好了!? 不過話說,在 Fix 這個 bug 的時候整個網頁伺服器上的服務都不能用,除了其中一個完全不依賴 Database 的服務,我想我不用說你也已經猜到了: ArOZ Online 系統!這套系統在 PHP Extension 一個都沒開 + MySQL 完全 offline 的情況下也能繼續正常運作還真的是滿壯觀的。 升級途中居然對 ArOZ 系統一點影響都沒有,還能夠一邊聽音樂一邊升級 就是這樣,伺服器就有驚無險地升級上 PHP7.4.4 囉! P.S. 有人問邊到睇到 MySQL 個 port你可以係 WAMP 個 menu 下面找 MySQL → Port used by MySQL: 3307
使用 PHP 啟動 Golang Binary 的 DIRECTORY SEPARATOR 問題
在開發 AOB 的時候不知道為啥 Windows 開發的 Websocket JWT 登入系統在 Linux 上跑不動,然後 Golang 寫的 WS Server 還不斷噴 Error,一開始還以為是 Golang 的問題,最後原來是啟動 Golang binary 的 PHP 的問題 。 Golang WebSocket Server 在 parse PHP 輸出的 JSON 時出現了 "<" 無法解釋所以 panic 了 在 Windows 上取得由 Web Root 到 Authentication Service Script 位置的代碼,Linux 上會回傳 "" (Empty String),必須移除有關 / 跟 \ 的處理部分才能使用。 //Parse the launch paramters if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { //Windows. Parse the launching path for validation services from wamp service webroot $fullPath = str_replace($_SERVER['DOCUMENT_ROOT'],"",realpath(str_replace("\\","/",__DIR__) . "../../jwt/validate.php")); $fullPath = str_replace("\\","/",$fullPath); $launchpath = str_replace($_SERVER['DOCUMENT_ROOT'] . "/","",$fullPath); }else{ //Life is always simplier on Linux $launchpath = str_replace($_SERVER['DOCUMENT_ROOT'] . "/","",realpath("../jwt/validate.php")); } 這問題主要出在 PHP 可以把 / 跟 \ 同時當作 "/" ,所以在程式裡作為 Directory Separator 用 "/" 就好,不需要特別處理,但是在 Golang 裡 "/" 跟 "\" 是不同的符號,必須要先做好預處理再 feed in 到裡面。 (你問我為啥不用 DIRECTORY_SEPARATOR ?因為測試過這東西也不能用啊?特別是要經過 realpath() 的時候特別容易出錯)
開發使用 Webcam 的網頁應用時找不到navigator.mediaDevices
如果你在開發使用 Webcam 的網頁的時候遇上這個錯誤: TypeError: navigator.mediaDevices is undefined Open Vtuber Studio 裡面顯示的錯誤 那是因為你在用 Firefox 而的網頁並非使用https 加密。然而你又太懶不打算幫你的實驗用網頁伺服器裝上 SSL 憑證,那你可以跟著以下步驟解決: 轉用 Google Chrome在網址列輸入 chrome://flags/#unsafely-treat-insecure-origin-as-secure 把你的網站加入到例外清單把此功能開啟(Enable) 在我工作室的測試伺服器設定例子 設定之後重啟一下 Chrome 即可解決。至於 Firefox 上這問題依然是無解,除非你自己重新組建一個 Firefox 出來。
目前第 4 頁,共有 5 頁