【发布时间】:2017-07-04 23:22:03
【问题描述】:
很长一段时间以来,我一直在想在哪里为我的 go 包保存资源,但我找不到正确的答案。
现在,资源被保存到
- main/module1/resources
- main/module2/resources
当我只使用 main 中的 go run *.go 时,它工作正常。但是,当我运行测试时,找不到资源,因为 Go 将当前工作目录更改为相应的模块。一种解决方法是只使用 os.Chdir('..') 但对每个测试都这样做似乎有点烦人。此外,有时资源会在包初始化时加载。这意味着有一个包含所有模板(及其相应名称)的全局地图:
package module2
var myTemplates = map[string]template { "index": loadTemplateFromFile() }
我知道你不应该因为并发而在 go 中使用全局变量,但是这个变量对于并发读取是安全的(并且它只在启动期间被写入)。所以我把模板放到一个全局变量中,这样它们就可以在启动时加载,并且在启动过程中会发现一些错误并且模板已经在内存中。
这意味着我无法及时更改工作目录,因为问题已经引起恐慌,并出现错误提示我找不到文件(这正是我想要的行为)。
后来,我发现了一个方法runtime.Caller 来获取编译go文件的路径(如果有的话):
base := "."
if _, filename, _, ok := runtime.Caller(0); ok {
base = path.Join(path.Dir(filename), "..")
}
file := path.Join(base, "module1", "resources", ...)
现在,所有测试都运行良好,go run *.go 也找到了正确的资源。一旦我开始部署我的包,这种方法就会成为一个问题。因为 runtime.Caller 使用编译期间使用的路径,所以我必须将资源放在编译它们的完全相同的位置。这意味着当我在 /home/matt3o12/go/src/domain.tld/matt3o12/mypackage 中构建二进制文件时,我必须将所有资源文件夹放在服务器上完全相同的路径中。
我也尝试使用os.Executable,但这毫无意义,因为它只是指向一些临时路径,其中包含已编译的可执行文件(并且没有资源)。除非我编译一个测试包并手动运行它,但这是非常繁重的工作。
我也不打算将资源存储在二进制文件中(具有项目资源的文件夹结构就可以了)。
我确实有一些想法可以解决这个问题,但我认为它们中的任何一个都不是可选的:
- 使用标志指向资源文件夹。这会起作用,但对我运行的每个测试都使用它会很烦人(因为现在每次我使用
go test ./...或go run *.go,我都必须担心正确的资源路径)。 - 使用“魔术”来确定测试是否运行并相应地更改路径。我不想使用它,因为我必须在我的代码中放置这些检查,而且我讨厌有很多“边缘/特殊”情况。
此不是以下内容的重复:
【问题讨论】:
-
查看github.com/jteeuwen/go-bindata 或其他类似选项将您的资源打包到可执行文件中。
-
在这个可能的副本中查看一些想法:how to reference a relative file from code and tests。
-
@superfell 我知道 go-bindata 但这不是我想要的。我不想将数据存储到 go 源文件中。
-
@icza 这个问题与我的非常相似,但问题已经涵盖了他在第一点的回答。我希望有更好的方法