在 gohugo 中,如何读取文件
Table of Contents
在 hugo 中,我们是根据 hugo 的内容管理规则来管理文件目录,比如页面文件、菜单、页面资源文件、页面渲染模板等。这些通过文件名称,结合 layout 的 lookup 规则、Page Resources、静态文件等规则,模板引擎就可以获取到相应文件。
但如果你想获取文件资源对应的那个操作系统文件?应该怎么办?
比如,你需要读取某个文件的内容,但它并不是模板,这时又应该怎么办?又或者你需要获取 data
目录下数据文件的修改时间?除了可以把这个时间保存在 data
目录下的某个json 文件这种方法外,还有其它方法吗?
答案是肯定的。
文件操作与路径操作的相关函数 #
gohugo 的使用了 golang 的 html/template 和 text/template 模板引擎,只要 hugo 置入相应的 golang 函数,那么是可以做的。在 hugo,有与文件操作与路径操作相关的函数。
- readDir
- 输入
路径字符串
,返回文件或目录列表
,对应的 golang 类型为os.FileInfo[]
,从当前工作目录下获取指定目录。 - readFile
- 输入
文件名称
,返回文件内容字符串
,从当前工作目录下,按指定的文件路径获取其中内容 - os.Stat
- 输入
文件路径
,返回文件对象
,对应的 golang 类型为os.FileInfo
,获取指定路径的文件信息 - path.Base
- 输入
文件路径
,返回文件名
,类似 linux 命令basename
- path.Dir
- 输入
文件路径
,返回路径字符串
,类似 linux 命令dirname
- path.Ext
- 输入
文件路径
,返回文件扩展名
- path.Join
- 多参数,输入
文件路径或文件名
,返回路径字符串
,根据操作系统类型拼接路径 - path.Split
- 输入
文件路径
,返回路径与文件名称
,相当于path.Dir
+path.Base
os.FileInfo #
上节中的 readDir
、os.Stat
的返回结果,都与 golang 本身的类型
os.FileInfo
有关。从这个 os.FileInfo
可以获取到如下信息:
// https://golang.org/pkg/io/fs/#FileInfo
type FileInfo interface {
Name() string // 文件名称
Size() int64 // 文件大小的字节数; system-dependent for others
Mode() FileMode // 文件权限 file mode bits
ModTime() time.Time // 文件修改时间,modification time
IsDir() bool // 文件是否是目录,abbreviation for Mode().IsDir()
Sys() interface{} // underlying data source (can return nil)
}
注意,这个
os.FileInfo
是 golang 中的结构体,gohugo 中针对文件也有一个变量类型,定义见 File Variables ,注意两者是有区别的。
函数的限制或约定 #
上述的函数中, path.Xxx
函数只是路径或文件名称的字符串操作,不涉及文件、路径是否存在的验证问题。而 readFile
、os.Stat
、readDir
则不同,如果文件不存在,会报类似『文件不存在』的错误,导致模板渲染失败。
文件或目录路径的位置问题 #
在 readFile
的文档页中有提到 a file from disk relative to the current project working directory
,在 readDir
的定义说明上指出 current working directory
,都在说明一个事实,readFile
、readDir
、os.Stat
这些文件操作相关函数,把 hugo 工程位置当成根目录。
本站的 hugo 工程目录是这样组织的,
~/Projects/youwu.today git:(develop)
➜ ls -1
Makefile
content
public
site
themes
~/Projects/youwu.today git:(develop)
➜ ls -1 site
config
config.toml
data
resources
static
~/Projects/youwu.today git:(develop)
➜ hugo server -s site
与一般的 hugo 例子不同,站长将 content
、themes
目录都移到了工程目录 site
之外。当然 hugo 的启动命令要添加不少对应的参数(不是这里的重点)。站长使用 hugo server -s site
来启动测试。那么上面的 readFile
、readDir
、os.Stat
对应的根目录就是个 site
目录。
使用 readDir 列出当前目录的文件列表 #
在模板中使用以下片段,
{{ range (readDir "." ) }}{{ printf "dir: %s, " .Name }}{{ end }}
得到类型这样的结果
dir: .DS_Store, dir: config, dir: config.toml, dir: data, dir: resources, dir: static,
如果使用一个实际不存在的路径,
{{ range (readDir "abc" ) }}{{ printf "dir %s, " .Name }}{{ end }}
会得到这个的错误:
Rebuild failed:
Failed to render pages: render of "home" failed: "/Users/youwu/Projects/youwu.today/themes/youwu/layouts/index.html:1:29": execute of template failed: template: index.html:1:29: executing "main" at <readDir "abc">: error calling readDir: failed to read directory "abc": open /Users/youwu/Projects/youwu.today/site/abc: no such file or directory
1 {{ define "main" }}{{ range (readDir "abc" ) }}{{ printf "dir %s, " .Name }}{{ end }}
2 <h1 style="display: none">今日有物(悟)的主页| homepage of youwu.today</h1>
使用
readDkir
时要注意,路径
对应的目录一定要存在,不然会报no such file or directory
或者not a directory
错误。
使用 readFile 获取文件内容 #
在测试模板中添加如下片段:
{{ readFile "config.toml" }}
上例读取了本站的默认文件 config.toml
,得到的结果如下(配置内容太长,只是截了前面一小部分作为演示,反正就是一个很长的字符串):
## 其它较固定的配置项在 `config` 目录中管理 title = "今日youwu | 今日有悟 | 今日有物" baseURL = "/" theme = ["youwu", "hargo"] themesDir = "../themes/" publishdir = "../public/" contentDir = "../content/" enableGitInfo = true ...
使用
readFile
时要注意,文件名
对应的文件一定要存在,不然会报no such file or directory
或者is a directory
错误。
使用 os.Stat 获取文件信息 #
这个例子中的文件信息,是指
os.FileInfo
在测试模板中添加测试片段:
{{ $file := os.Stat "config.toml" }}
{{ print "修改时间: " $file.ModTime }}
{{ print ",文件大小: " $file.Size }}
得到类似以下的信息
修改时间: 2021-05-25 17:03:46.272216256 +0800 CST ,文件大小: 2422
os.Stat
的输入参数可以是目录名称、也可以是文件名称,若不存在,会报 file does not exist
错误。