Hexo框架图片引用处理的两种思路和原理分析

图片引用的处理是用hexo写博客最大的痛点之一。适当地修改hexo配置文件配合一些插件则能很好地解决这个问题。

基本思路

要在自己的博客上显示图片有两种思路:

  • 使用图床
  • 将图片放在自己的服务器上并正确设置引用路径

我写博客主要还是以日常记录为主,因此需要在没有网络的情况下也能正常浏览。所以采取第二种方法来管理图片。

想法很简单,为每个页面创建一个资源文件夹,编辑器和hexo框架帮我自动处理图片等资源的复制和路径的重写,我只需要将图片拖入编辑器。

尝试过各种方法之后我发现Typora+Hexo插件的方案可以完美满足我的需求。

图片引用问题

Hexo配置

Hexo框架支持为每个页面建立资源文件夹的管理方式。在根目录下的_config.yml文件中将post_asset_folder选项设置为true打开该功能:

1
2
_config.yml
post_asset_folder: true

更多信息见Hexo官方文档对应章节1

编辑器设置

前面已经提到,编辑器最好能帮我们把图片复制到指定文件夹并设置好相对引用路径。

以我用的Typora为例,打开文件->偏好设置->图像,按照图中的样子设置。

设置完成后每次你在文档中插入图片时编辑器就会自动将图片复制到同名文件夹中,并将图片引用设置为相对路径。这在后面的插件处理中非常有用。

图片引用重定向

关于图片引用的处理也有两种思路:

  • 在Hexo开始渲染之前对Markdown文档进行预处理
  • 对Hexo生成的html文件进行处理和改写

前面一种思路相对简单一点,而处理html文件的方法更复杂但是功能也更强大。两种方法都有写好的插件可以用。

对于思路一,可以用hexo-image-link插件。具体使用方法见GitHub项目地址:

cocowool/hexo-image-link: 当MD中引用本地文件时,处理生成的html中的图片链接。 (github.com)

思路二可以用hexo-asset-image插件。详细信息同样可以在GitHub项目主页找到:

xcodebuild/hexo-asset-image: Give asset image in hexo a absolutely path automatically (github.com)

如果你在使用插件的过程中遇到了各种问题,请看下面的插件原理分析,相信看完插件原理之后你就会找到解决方案

插件原理分析

hexo-image-link插件

到GitHub主页上看一看hexo-image-link插件的源码可以发现,这个插件做的事情就是正则表达式识别图片引用并改写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
'use strict';

// match markdown image and covert to asset_img
hexo.extend.filter.register('before_post_render', function(data){

// 正则表达式匹配Markdown标准图片引用格式
data.content = data.content.replace(/!{1}\[([^\[\]]*)\]\((\S*)\s?(?:".*")?\)/g,
function(match_str, label, path){

// if only one /
if( (path.split("/")).length == 2){
console.debug("Markdown Image Path: " + match_str);
console.debug("asset_img string: " + "{% asset_img " + (path.split("/"))[1] + " " + label + " %}" );
// 将原来的图片引用由![label](img)改写为{% asset_img img label %}的格式
return "{% asset_img " + (path.split("/"))[1] + " " + label + " %}"
}else{
console.debug("Markdown Image Path does not exists!");
return match_str;
}

});

return data;
});

Markdown标准图片引用格式为![label](img),因此插件中使用/!{1}\[([^\[\]]*)\]\((\S*)\s?(?:".*")?\)/g这一个正则表达式匹配所有的图片引用,再将图片引用改写为{% asset_img img label %}的形式。这样,Hexo在渲染时就会将图片路径改写为相对路径。

由此也可以看出这种思路的优缺点:优点在于逻辑简单不容易出错,缺点在于功能不够强大,例如在编辑器中对图片进行缩放之后图片的链接将会变成html格式,此时这个插件就没法正确识别了。

hexo-asset-image插件

这个插件最关键的地方在这几行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var link = data.permalink;
var beginPos = getPosition(link, '/', 3) + 1;
var appendLink = '';
// In hexo 3.1.1, the permalink of "about" page is like ".../about/index.html".
// if not with index.html endpos = link.lastIndexOf('.') + 1 support hexo-abbrlink
if(/.*\/index\.html$/.test(link)) {
// when permalink is end with index.html, for example 2019/02/20/xxtitle/index.html
// image in xxtitle/ will go to xxtitle/index/
appendLink = 'index/';
var endPos = link.lastIndexOf('/');
}
else {
var endPos = link.length-1;
}
link = link.substring(beginPos, endPos) + '/' + appendLink;

首先读取博客文章的链接data.permalink,随后识别链接截取文章的资源文件夹的绝对路径,最后用这个绝对路径加上图片文件名来改写html文件中的图片引用。

注意到识别部分有一个条件判断,对Hexo框架有一定了解的话一定会知道其中的原理:

  • 一些特殊页面,比如关于页面,是放在一个单独的目录下的,页面源码在目录下的index.html文件中。插件中使用判断是否以index.html结尾来识别出这类页面。例如本站的关于页面路径为https://donnadie.top/about/index.html,那么插件就会将该页面中的图片的路径改写为https://donnadie.top/about/index/image-path.png
  • 对于普通页面,例如本文的文章路径就是https://donnadie.top/hexo-url-and-asset/。插件将会把本文中的图片路径改写为https://donnadie.top/hexo-url-and-asset/image-path.png
踩坑提醒

细心如你一定已经发现,该插件的识别机制是相当简单的,在面对不同情况时可能会出错。

说说我自己踩过的一个坑吧,由于Hexo默认根据文件名生成URL,而我为了方便管理文件名都用的是中文,因此URL中出现了大量转义字符很不美观。网上的教程让修改hexo配置文件自定义文章的permalink,详细情况见这几篇博客:

hexo博客进阶(一)url设置和百度url提交、百度访问量统计_玖涯博客-CSDN博客

hexo 自定义文章 url 地址 - WeJan’s Blog (nullpointer.pw)

按照这些教程提供的方法,本文的URL会变成https://donnadie.top/hexo-image-processing-analysis.html,这样一来hexo-asset-image插件的处理显然是会出错的。不过,如果你已经弄懂了上面的原理,简单修改一下插件的源码就能解决报错。

BTW,网上提供的这些自定义URL地址的方法都在末尾添加了.html这个后缀,而今天的主流Web服务器早就不需要通过后缀来判断请求内容,我们的URL也可以变得更优美一些。关于自定义URL的内容可以看这篇博客

参考资料

[1] 资源文件夹 | Hexo