不可靠的文件系统
在给电脑重装完系统以后,我就把自己博客源码迁移过来了。一开始没什么,后面我发现了一个很诡异的事:
日期怎么变成这样了,更新日期比发布日期还提前。肯定不对。
我发现这里更新日期其实就是这篇文章的“发布日期”,因为这篇是Hello World,是搭建的时候系统自动生成发一篇文章,所以也相当于是建站时间。
那么究竟是为什么呢?
其他的文章都没出问题,就这个出问题了。而且出现在迁移后这个时间点。
于是我简单了解了一下Hexo框架对于文章的更新和发表日期是如何确定的,得出了一个结论
对于发布日期:它会去读Front-matter里的date参数,如果有就直接把它的值作为发表日期;否则,将会采用文件系统里的文件元数据中的 文件创建日期 作为发表日期。刚好,系统给的示例文章Front-matter是不带date参数的
对于更新日期:它会直接读文件元数据中的“修改日期”字段
因为示例文章(Hello world)里面是没有date信息的,所以它的降级策略会去看文件元数据。这样这个诡异问题的源头就在文件元数据上。
如果是刚创建出来的文件,那么它的创建日期和修改日期应该是一致的,修改日期就是创建日期。
那么如果是从网上下载的文件呢?
这个取决于下载器的实现方式了,
如果是分片的话下载的时候会产生临时文件,而这些文件就是原始文件的各个不同的区域的数据。完全下载后会创建最终的结果文件来把这些分片合并进去,创建时间便是这个分片即将开始合成的时间。修改时间是合成完毕后的时间。
如果是直接下载整个文件的话,一般情况下都会先创建一个这个文件的临时文件(Edge浏览器的未确认文件),然后下载过程中往里面动态填数据。这个时候创建时间是这个临时文件诞生的时间。对于浏览器,修改时间有时候会在HTTP响应头部透传服务器端的该文件的修改时间,否则便是下载完成后的时间。
从上面我们可以看出只要文件被上传到了网上,再想下载下来原始的元数据就基本上就“破坏”的差不多了。非常不可靠。(更别说什么时区问题了)
这时候你肯定有个疑问:既然无论怎么样这个修改日期和创建日期都会变成下载过程中或者下载完成后的时间,那么你这篇Hello World的修改日期为什么保存的那么好?
这是因为我备份肯定是压缩备份的,备份肯定是备压缩包。上述下载的创建和修改日期在这里是基于压缩包这一整个备份文件而言的。
所以这个问题实质上是压缩文件解压缩后的文件的时间相关元数据是如何确定的。
压缩过程中,因为修改日期这个数据在文件里,所以压缩的时候会随着原始文件一起被完整打包,解压缩的时候释放出来的文件将会保留压缩前原始文件的“修改时间”元数据,因为解压缩软件会调用操作系统API把这个属性写回解压后的文件。
而创建时间的确定就很坑了:解压的时候是创建一个和原来的文件看起来一样的新文件,然后再往里面填充数据。所以这个解压缩的实质是新建。压缩的时候原始文件的“创建时间”的属性将被丢弃。
于是最终会变成这样:
Hexo框架生成时,读取了“创建时间”作为发表日期,读取了“修改时间”作为更新日期。造成了时间穿越的假象。
目前已经修复,把那篇系统文章手动加上了Front-matter参数,不再走文件系统的时间。
延伸思考:Hexo将文件的“修改时间”元数据作为文章的更新时间是否可靠?
我认为这是一种很直接的办法,因为文章的markdown文件被修改了就说明肯定被动笔了。而文件系统会在编辑器类的软件调用接口保存文件的时候更新这个文件元数据。所以读取修改时间作为更新时间最直接,也是能证明更新的可靠又优雅的思路。要是靠博主每次编辑完都要手动修改那个修改日期的参数既不优雅也容易出问题(写作的人会忘,也嫌麻烦)
除非有一种可能会出问题:就像我正文说的那样,上传和下载原始markdown文件,不打包为压缩包。这样新的下载副本的修改日期就难以确保可靠性了。
我的博客架构是构建输出和构建原件分离的设计。Github仓库只存产物,构件在电脑本地。如果要一劳永逸的解决这个问题的话那就用那种时间日期的插件,直接抛弃文件本地时间。采用首次 git commit 的时间作为文章的创建日期,采用最近一次 git commit 的时间作为文章的最后更新日期。不过我目前既然技术选型这样已经用了好久了,如果强换的话反而会破坏现有的更新时间。(好像早在迁移的时候就已经破坏的差不多了,懒得搞找借口哈哈)


