我们有一个程序,人们可以在他们的机器上编译。它有一个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
我已经搜索过,到目前为止只找到了这个: 如何在 Go 中使用相对路径打开文件?
但这完全不适用于我们。 另一种解决方案建议捆绑编译的 go 资源,但这似乎很奇怪,因为错误页面是 html 文本。
我们也尝试过 https://stackoverflow.com/a/31464648/169252
但它具有相同的效果。
我们如何确保路径始终正确解析?这似乎不应该太难做到,但到目前为止我们还没有做到。
编辑:这不是问题的精确副本 如何在Go中使用相对路径打开文件?.正如我的问题文本中已经提到的,我已经自己查过了。建议使用filepath.Abs()
.但正如我的问题中所述,对我们来说这是行不通的,就好像我们的可执行文件是从不同的地方调用的,filepath.Abs()
不返回相同的值,因此对我们不起作用。
我认为您在这里面临的挑战是人们可以将程序安装在磁盘上的任何位置,并且该程序必须足够智能才能知道它以后的位置。
我见过的常见方法之一是人们通常使用environment variables
将它们锚定到应用程序的安装路径。我相信您可能已经看到过具有*_HOME
命名模式的环境变量,例如JAVA_HOME
、MAVEN_HOME
并且它们的值始终是安装位置的文件路径。
我想你可以在这里做同样的事情。强制用户定义MYAPP_HOME
变量,并在应用程序开始时确保已设置该变量,否则将引发错误,指出未设置MYAPP_HOME
。
然后,您需要做的就是简单查找模板 html 文件的源MYAPP_HOME
+/http/tmpl
的值。
例:
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"
}
如果您不热衷于使用当前工作目录或传入目录,则可以通过调用 os 找到绝对可执行路径。操作系统包中的可执行文件。
appPath, err := os.Executable()
操作系统包通常会包含特定于操作系统的内容,例如如何获取当前工作目录。如果您遇到困难,值得查看 pkg 文档和 golang.org 软件包列表,因为它们非常好,通常您会在那里找到答案。
https://golang.org/pkg/os
如果用户使用 go get 安装,您可以在此处采取的另一种方法是依靠以下事实:您的模板将与 gopath 下的 pkg 一起安装,因此您始终可以在 $GOPATH/src/your/project/path/templates 中找到它们(或 ~/go 默认的 gopath,因为它不是严格要求的)。
最安全的方法可能是将它们与虚拟文件系统中的二进制文件捆绑在一起,因为这意味着您不依赖于外部,也不关心您的应用程序托管在哪里,甚至根本不关心它是否可以访问文件。
在这种情况下,我建议使用相对路径。
根据您的描述,您似乎正在开发一个 Web 应用程序。虽然它在单个开发人员的计算机上运行良好,但您需要注意,您的应用程序可以部署在生产服务器上的任何目录下。您无法确定应用的部署位置,但始终可以确定静态文件相对于应用根目录的位置。
在命令行中调用应用时,应将所有必需的静态文件复制到与开发环境完全相同的相同相对路径。我的典型结构是:
project/
|- config.json
|- main.go
|- package1/
|- package2/
|- static/
|- templates/
| |- index.html
| |- base.html
|- css/
|- javascript/
|- image/
当您准备好从命令行运行应用程序时,请务必将 config.json 和 static/目录复制到与可执行二进制文件相同的级别。然后,您需要做的就是在代码中使用相对路径,而不会造成任何噩梦。
对于记录:由于我们只有两个模板,因此我们将它们作为字符串存储在 go 文件中,以便对其进行编译。我们的 html 模板非常简单,所以这是一种合理的方法。