跳到主要内容
  1. Skills/
  2. Hugo 使用指南/

在 gohugo 中,如何读取文件

·字数 1694·4 分钟
howto

hugo 中,我们是根据 hugo 的内容管理规则来管理文件目录,比如页面文件、菜单、页面资源文件、页面渲染模板等。这些通过文件名称,结合 layout 的 lookup 规则Page Resources、静态文件等规则,模板引擎就可以获取到相应文件。

但如果你想获取文件资源对应的那个操作系统文件?应该怎么办?

比如,你需要读取某个文件的内容,但它并不是模板,这时又应该怎么办?又或者你需要获取 data 目录下数据文件的修改时间?除了可以把这个时间保存在 data 目录下的某个json 文件这种方法外,还有其它方法吗?

答案是肯定的。

文件操作与路径操作的相关函数 #

gohugo 的使用了 golanghtml/templatetext/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 #

上节中的 readDiros.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.FileInfogolang 中的结构体,gohugo 中针对文件也有一个变量类型,定义见 File Variables ,注意两者是有区别的。

函数的限制或约定 #

上述的函数中, path.Xxx 函数只是路径或文件名称的字符串操作,不涉及文件、路径是否存在的验证问题。而 readFileos.StatreadDir 则不同,如果文件不存在,会报类似『文件不存在』的错误,导致模板渲染失败。

文件或目录路径的位置问题 #

readFile 的文档页中有提到 a file from disk relative to the current project working directory ,在 readDir 的定义说明上指出 current working directory,都在说明一个事实,readFilereadDiros.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 例子不同,站长将 contentthemes 目录都移到了工程目录 site 之外。当然 hugo 的启动命令要添加不少对应的参数(不是这里的重点)。站长使用 hugo server -s site 来启动测试。那么上面的 readFilereadDiros.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 错误。