2017-08-08 123 views
0

我們有一個程序,人們可以在他們的機器上編譯。它有一個HTTP接口,但也可以通過命令行調用。加載需要的文件,相對於絕對路徑

爲了給HTTP客戶端提供漂亮的錯誤頁面,我們希望提供錯誤頁面。我們使用go html/template軟件包,使用了非常簡單的解決方案。

因此,爲了使程序找到模板,我們目前正在做的事:

func init() { 
    prefStr := "path/to/http/tmpl" 
    pathPrefix,err := filepath.Abs(prefStr) 
    if err != nil { 
    log.Warn("Template path %s is not available!", prefStr) 
    } 

    pathPrefix + "/err.html" 
} 

現在調試應用程序時,這通常效果很好 - 我們是在包的根目錄下,這樣filepath.Abs()正確解析,像這樣: $GOPATH/github.com/user/repo/path/to/http/tmpl(正確擴展$GOPATH

但是,當我們通過命令行通過可執行文件調用應用程序時,這是行不通的。命令行當然可以從文件系統的任何地方調用,以方便例如提供當前目錄中的文件作爲參數。

簡而言之,運行/some/other/path/on/fs/our-executable filename.txt導致上述init()函數因目錄錯誤拼接而中斷:它需要/some/other/path/on/fs/來創建絕對路徑,這是錯誤的。因此,它與 panic: open /some/other/path/on/fs/path/to/http/tmpl/err.html: no such file or directory

我已經搜查,到目前爲止,只發現這個崩潰: How can I open files using relative paths in Go?

但這正是並不適用於我們。 另一種解決方案提出捆綁編譯的go資源,但這看起來很奇怪,因爲錯誤頁面是html文本。

我們也試過 https://stackoverflow.com/a/31464648/169252

,但它具有相同的效果。

我們如何確保路徑始終正確解析?這似乎是一件不應該太難完成的事情,但我們目前還沒有成功。

編輯:這不是問題How can I open files using relative paths in Go?的確切副本。正如我在問題文本中已經提到的那樣,我已經自己查閱了它。它建議使用filepath.Abs()。但正如我的問題所闡明的那樣,對我們來說這不起作用,就像我們的可執行文件被從不同的地方調用一樣,filepath.Abs()不會返回相同的值,因此對我們不起作用。

+0

'path/to/http/tmpl'是絕對路徑嗎?這意味着它從文件系統的根目錄開始。例子'/ home/faboolous/abc/defg/tmpl' –

+0

@ SamuelToh不,它不是絕對路徑,否則它不能移植。這是從回購的根源的相對路徑。 – faboolous

+0

使用[go-bindata](https://github.com/jteeuwen/go-bindata)將模板編譯爲go二進制文件 – Mark

回答

0

我認爲你的挑戰在於人們可以在磁盤上的任何位置安裝該程序,並且該程序必須足夠聰明才能知道它以後的位置。

我看到的一種常見方法是人們通常使用environment variables將它們錨定到應用程序的安裝路徑。我相信您可能已經看到*_HOME的命名模式(如JAVA_HOME,MAVEN_HOME)的環境變量,並且它們的值始終是安裝位置的文件路徑。

我想你可以在這裏做同樣的事情。強制你的用戶擁有MYAPP_HOME變量定義,並且在應用程序的開始時確保它已被設置,否則拋出一個錯誤,說MYAPP_HOME沒有設置。

然後,您只需要簡單查找MYAPP_HOME + /http/tmpl的值以獲取模板html文件的值。

例子:

package main 

import "os" 

func main() { 
    // Assuming MYAPP_HOME has been verified that it is set 
    // Then: 
    tmlPath := os.Getenv("MYAPP_HOME") + "/http/tmpl/" 
    errTml := tmlPath + "err.html" 
} 
+0

我明白這個解決方案,感謝您的建議。 Go沒有真正的方法來獲取文件存儲的當前目錄嗎? – faboolous

+0

我不認爲這是因爲您的產品的二進制文件可以安裝在文件系統的任何位置,並且您無法預測用戶將要放置的位置,除非您明確要求它們將其安裝到某個位置。然後你可以'將你的代碼的路徑硬編碼,這是不好的。 –

+0

即使在'go'中有一個API可以神奇地在用戶的系統上搜索名爲'tmpl.html'的文件,我在想如何驗證tmpl文件確實是我們之後呢?因爲文件名不是全球性的。那麼我們可以做'校驗和',但也許它有點過分殺,肯定會讓性能受到影響...... –

0

如果你不熱衷於使用當前工作目錄,或通過該目錄中,您可以通過從OS軟件包調用os.Executable找到絕對的可執行文件的路徑。

appPath, err := os.Executable() 

操作系統軟件包通常將包含類似如何獲取當前工作目錄的操作系統特定的東西。在golang.org上查看pkg文檔和軟件包列表是值得的,因爲它們非常好,通常你會在那裏找到答案。

https://golang.org/pkg/os

如果用戶去得到安裝,你可以在這裏採取另一種方法是依靠事實,你的模板將與下GOPATH的PKG安裝,讓您可以隨時在$ GOPATH/src目錄找到它們/你的/項目/路徑/模板(或〜/現在默認gopath,它不是嚴格要求)。

最安全的方法可能是將它們與二進制文件捆綁在一個虛擬文件系統中,因爲這意味着您完全不依賴於外部,並且不關心託管應用程序的位置,甚至根本無法訪問文件。

0

我推薦在這種情況下使用相對路徑。

根據您的描述,您似乎正在開發一個Web應用程序。雖然它在個人開發人員的機器上可以正常工作,但您需要注意,您的應用程序可以部署在生產服務器上的任何目錄下。您無法確定應用程序的部署位置,但始終可以確定靜態文件相對於應用程序根目錄的位置。

當您在命令行中調用您的應用程序時,應該將所有必需的靜態文件複製到與您的開發環境完全相同的相同路徑。我的典型結構是:

project/ 
    |- config.json 
    |- main.go 
    |- package1/ 
    |- package2/ 
    |- static/ 
     |- templates/ 
     | |- index.html 
     | |- base.html 
     |- css/ 
     |- javascript/ 
     |- image/ 

當您準備從運行命令行應用程序,請務必將config.json和靜態/目錄拷貝到同一級別的可執行二進制文件。那麼你所需要做的就是在你的代碼中使用相對路徑而不做任何噩夢。

0

對於記錄:因爲我們只有兩個模板,我們採取將它們作爲字符串存儲在go文件中以便它們進行編譯。我們的html模板非常簡單,所以這是一個合理的方法。