Golang os/exec 使用方法(筆記)
此筆記是一個簡單純粹的快速參考目錄,方便我(或是你?)在開發時使用 os/exec 之用。如需要詳細的教學或 API 資料,請直接參閱 https://golang.org/pkg/os/exec/ 此文中有部分例子參考自: https://colobu.com/2017/06/19/advanced-command-execution-in-Go-with-os-exec/ 執行指令並回傳結果 func main() { cmd := exec.Command("ls", "-lah") out, err := cmd.CombinedOutput() if err != nil { log.Fatalf("cmd.Run() failed with %s\n", err) } fmt.Printf("combined out:\n%s\n", string(out)) } 加入額外的環境參數再執行指令 cmd := exec.Command("programToExecute") additionalEnv := "FOO=bar" newEnv := append(os.Environ(), additionalEnv)) cmd.Env = newEnv out, err := cmd.CombinedOutput() if err != nil { log.Fatalf("cmd.Run() failed with %s\n", err) } fmt.Printf("%s", out) 指定 CWD 並執行指令 cmd:= exec.Command("git", "log") cmd.Dir = "your/intended/working/directory" out, err := cmd.Output() 注意:使用上術的方式時請確保相關的指令存在於 ENV 參數內(或 Windows 的 %PATH%),如果不是,請使用 binary 的 Absolute Path,例如: abspath, _ := filepath.Abs("./mybinary") cmd:= exec.Command(abspath, "log") cmd.Dir = "your/intended/working/directory" out, err := cmd.Output() 在 Linux 下使用 os/exec + Pipe 或 Bash 運算式 rcmd := `iw dev | awk '$1=="Interface"{print $2}'` cmd := exec.Command("bash", "-c", rcmd) out, err := cmd.CombinedOutput() if err != nil { log.Println(err.Error()) } log.Println(string(out)) 在 Windows 下使用 os/exec + Batch 運算式 cmd := exec.Command("cmd", "/c", "ffmpeg -i myfile.mp4 myfile.mp3 && del myfile.mp4") out, err := cmd.CombinedOutput() if err != nil { log.Println(err.Error()) } log.Println(string(out)) 使用 String Slice 為指令傳進 os/exec args := []string{"hello", "world"} cmd := exec.Command("echo", args...)…
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: 協議
高自由度的 Web Desktop 服務?
說到高自由度的網上平台,你會想起哪一些網頁 / 平台? Google Drive? Facebook? Youtube? 還是說可以 post 奇怪照片的網站 Twitter(?) 平台自由度與用途 一般網頁都不會給予用家太高的自由度,這理由很簡單,一個網頁就是為了提供一種服務,無論那種服務所覆蓋的範圍是廣還是窄,以下是一些例子: draw.io → 網上圖表制作easyEDA → 網 上 PCB 制作與設計onlinesequencer → 網 上音樂制作facebook / twitter → 網 上社交分享Youtube / viemo / po***** → 網 上影片分享平台Office 365 / Google Document → 網上辦公軟件 所以,以商業角度來說,每一個平台必須要有他們的獨立功能才能夠有辦法營運,然而這亦會導致一些問題,就是平台依賴性 (Platform dependencies) 跟 供應商鎖定 (Vendor Lockdown) 問題。 為甚麼我應該擔心這些問題? 由於這篇文章不是說關於這些 WebApps 的問題,所以我也不詳細解釋這些問題了。簡單來說就是: 如果 A 網頁系統用的格式跟 B 網頁系統用的格式不一樣,你是沒辦法把檔案移動到另一平台上面如果提供 A 網頁系統的公司倒閉了,那你使用 A 系統編寫的檔案全部都再用不了 要解決這個問題,除了使用開放格式 / 標準格式(如 IEEE 制定的格式)外,就是開源系統本身了。但是, 真的這麼不幸擁有 A 系統的公司倒了,我想應該不太多人會懂得用開源的系統重建一套新系統出來吧?比較有可能的是會有人開發一個轉接器把 A 系統的格式轉成另一家 B 系統,但是這個也不是這麼容易可以做到,特別對於重度依賴 A系統並有大量 A 系統格式的檔案的人來說。有甚麼可能更槽糕?就是連檔案本身也是儲存在 A 系統裡面的,當 A 系統倒的時候檔案就跟著系統消失了。 以本地運作為前提設計的 WebApp 所以另一個想法,也就是 ArOZ Online 裡面一直在用的系統,就是把 WebApp 的擁有權取回來的方法。由用者擁有 WebApp 而不是由網頁提供者所擁有。這樣,就算供應該服務的公司倒了,WebApp 依舊能用,檔案也不會消失。例如好像說能在主機啟動的時候檢查更新,並把 WebApp 模組下載到啟動資料夾之類的。當然,你也可能會問: 這不就是 React Native 了嗎? 不對喔! (´・ω・`) 這裡就是 ArOZ Online 設計的不同了。React Native 針對的是本地的作業系統,例如 iOS,Windows 或是 MacOS,不同的作業系統會有不同的安裝檔,裡面各包一個 Chromium。那既然是這樣,那為甚麼不直接讓用家在 Chromium ( 或 Firefox / Chrome /Edge 之類的瀏覽器)打開你的 WebApp 呢?這樣不但省空間還能直接用標準 Web Standard 來開發模組。 而這個就是 ArOZ Online 的設計理念:屬於自己的 WebApp 伺服器;而且由於是使用 Web Protocol 傳送的,所以也跟其他 WebApp 一樣去到哪裡,甚至不需要是自己的電腦,都可以透過網絡存取到 WebApp 的內容。 論高自由度平台的條件 然而,因為這種設計已經脫離了一般 Web 開發的範圍,所以要開發一個這麼高自由度,多用途的系統會產生一大堆的問題,例如說 要重新設計 WebApp要想辦法解決本地檔案儲存問題怎樣以人性化的方式尋找及啟動 WebApp (aka 代替 Google 找 WebApp 的過程)怎樣讓使用者不用學習也能適應使用這套系統? 嗯?你有沒有覺得這些要求跟你平常在用的東西很像?沒錯,那就是作業系統 (Operating System),而作業系統本身也的確是一個很高自由度的東西,你可以把它用作 Media Center,可以用來辦公,遊戲,開發等等的用途。 我們需要的是一個能夠在網頁上運作的作業系統! 這個也是我開發 ArOZ Online 的理由:把自己需要的服務放到 Web Desktop 上,那去到世界各地,只要有網絡連接都能用自己的系統和存取到自己主機上的檔案。這跟 NAS 系統很像,只不過不同的地方在於這系統不單單用來放檔案,它亦能用來做其他事情。 這說起來簡單,做起來卻很複雜。這是因為自由度太高會導致很想意想不到的錯誤。好像說只是要處理使用者上載的檔案名稱就已經是一個大麻煩了。由於要跟其他系統兼容,所以不能隨便把檔案改名然後把原檔名放進資料庫這種 wordpress 般的操作;也不能讓使用者隨便把 ../ 放進去檔案路徑裡,就結果而言就是很多很多的麻煩。(有興趣的話可以參考這篇文章) 但是,經過了 Beta + 1.0 preview 差不多 5 年的開發和測試,最終於出來的效果也超乎了我的想像:一個用起來跟本地作業系統一樣的高自由度網頁桌面系統! 很有 Ubuntu…
使用 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)
ArOZ Online Pre-1.0 檔案存取路徑虛擬層
ArOZ Online BETA 與 Pre 1.0 版本的差別 在 ArOZ Online Pre-1.0 裡面,我幫 ArOZ Online 系統加上了一層虛擬層;你可能會問:為甚麼要加這個東西?這真是一個好問題,如果不是我開發過一個這樣的系統我也不知道檔案路徑虛擬化的重要性 問題一:檔案路徑安全性 一般如果你用 PHP 來寫系統,你很容易會使用一個檔案的真實路徑。舉個例子,你想讓用家選擇一個檔案,然後打開讓用家下載那個檔案,一般來說你會使用 realpath() 這個功能。然而,這有一定的危險性,例如有人在傳給伺服器的資料中加入 "../" ,讓他能離開你指定的存取範圍。 在 ArOZ Online Beta 的 implementation 中,由於所有檔案都是使用真實路徑存取的,這導致使用者很容易就可以在 path 裡面偷塞一點奇怪的字符以存取一些本來不應該被存取的東西。當然,你可以加一點東西來避免這問題,好像 Stack Overflow 上就有這樣的答案 Stack Overflow 上避免 PHP Directory Traversal 的方法 然而,每次都要透過一個特定的 function 來處理,總會有一天出問題的。(好像說某個開發者忘了用這個 function 或是這個 function 本身有 bug) 問題二:外置存取裝置 由於 ArOZ Online Beta 系統由原本的小網頁變成了一個類似作業系統的物體,它也需要去管理外置的存取裝置(如外接硬碟,或是 www root 所 mount 在的硬碟)。然而 PHP 並不是設計來做這樣的跨硬碟存取的,所以自然有不少的 Bug 存在,而開發起需要存取外置裝置的時候也特別麻煩,需要為。 在 ArOZ Online Beta 的 implementation 裡,使用者需要在 Apache conf 裡面設定 modXSendFile 來讓 PHP 可以在外置硬碟提供(serve)大型檔案如影片,音樂等等。然而,由於外置存取位置的路徑被直接寫進了 Apache conf,即使使用者想更改存取路徑也沒有辦法簡單完成。 解決方案:檔案路徑虛擬化 甚麼是路徑虛擬化?簡單來說就是把 file 的存取路徑進行轉換,讓不同的裝置也能夠輕易的讓模組使用 Virutalized path 來存取。簡單來說是這樣: 請求 Virtual Path模組把 Virtual Path 掉到 file system 的轉換 function回傳真實路徑處理資料 以 ArOZ Online Pre-1.0 的 File system listdir 功能來說,先假設你有兩隻 HDD mount 在了 /media/storage1 跟 /media/storage2,並把 storage.json 設定成以下的樣子: [ { "name":"Storage 1", "uuid":"S1", "path":"/media/storage1", "access":"everyone", "hierarchy":"users", "automount":true }, { "name":"Storage 2", "uuid":"S2", "path":"/media/storage2", "access":"everyone", "hierarchy":"public", "automount":true } ] 然後你可以傳入以下虛擬路徑用來處理檔案 S1:/video/ S2:/ user:/Music user:/Desktop/test 然而實際上,上術的虛擬路徑卻是在存取以下的真實路徑 /media/storage1/users/{username}/video /media/storage2/ ./files/users/{username}/Music ./files/users/{username}/Desktop/test/ 而設定當中,hierarchy 就是決定了虛擬路徑構造的元素,裡面的原理有點複雜,但是作為開發者,你只要存取 virtualPathToRealPath() 這個功能就能夠取得真實路徑了,簡單吧?
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 了。有夠簡單吧?
目前第 7 頁,共有 11 頁