hexo博客:next主题搭建二级分类相册

  今天突然想给博客搭建相册,浏览了不少关于hexo搭建相册教程,发现很多下面这样子的,还是很漂亮的。自己尝试了一番,相册需要缩略图和原图两个文件夹,其中缩略图都是裁剪好后的正方形,感觉不是自己想要的。

http://www.lawlite.me/photos/

(图)不带分类的相册

  后来发现给hexo静态博客添加动态相册功能hexo博客添加一级分类相册功能两位大佬的博客相册为两级结构,不用一下子加载整个网页的图片,很喜欢这种布局。于是花费两天时间,参考Hexo NexT主题添加多级相册功能,搭建了属于自己的相册。

整体结构

  本文相册是根据腾讯COS实现的,先创建相册一级目录和相册模板, 运行编写好的blog_cos.py文件,自动将下载的图片数据存放到galleries.yaml文件中,并根据腾讯COS相册文件结构创建对应的的二级相册目录。

- /blog/source_data/galleries.yaml  存放所有图片数据
- /blog/source/photos    相册一级目录
    - /blog/source/photos/index.md        相册一级目录对应的index.md
    - /blog/source/photos/WLOP            相册二级目录
    - /blog/source/photos/尼尔机械纪元    相册二级目录
    - ...
    - /blog/source/photos/blog_cos.py    从腾讯COS下载图片数据并创建相册二级目录
- /blog/themes/next/layout/photos.swig    一级相册模板
- /blog/themes/next/layout/photo.swig    二级相册模板

腾讯COS相册存储为两级结构,如我的腾讯COS在galleries文件夹下创建WLOP、尼尔机械纪元、大学生活等子文件夹。

准备工作

  • 打开/blog/themes/next/languages/zh-CN.yml文档,menu下添加中文相册
zh-CN.yml
1
2
menu:
photos: 相册
  • 修改主题配置文件/blog/themes/next/config.yaml
config.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 添加相册css风格,如果已有则不需要创建
# Define custom file paths.
# Create your custom files in site directory `source/_data` and uncomment needed files below.
custom_file_path:
head: source/_data/head.swig
#header: source/_data/header.swig
sidebar: source/_data/sidebar.swig
#postMeta: source/_data/post-meta.swig
#postBodyEnd: source/_data/post-body-end.swig
#footer: source/_data/footer.swig
#bodyEnd: source/_data/body-end.swig
#variable: source/_data/variables.styl
#mixin: source/_data/mixins.styl
style: source/_data/styles.styl

# 腾讯COS相册链接
photos_info:
cos_url: https://hexo-1257031621.cos.ap-chengdu.myqcloud.com/galleries/

# 添加相册
menu:
home: / || home
#about: /about/ || user
tags: /tags/ || tags
categories: /categories/ || th
archives: /archives/ || archive
photos: /photos/ || image
#schedule: /schedule/ || calendar
#sitemap: /sitemap.xml || sitemap
#commonweal: /404/ || heartbeat
  • source/_data/styles.styl文件末尾添加相册css风格
styles.styl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//相册风格
.gallery-wrapper{
padding-top: 20px;
padding-left: 5%;
margin-top: 20px;
}

.gallery-wrapper .gallery-box{
padding: 5px !important;
margin-bottom: 20px;
}

.gallery-box{
width: 30%;
margin-left: auto;
left: auto;
right: auto;
float: left;
}

.gallery-wrapper .gallery-item {
display: block;
overflow: hidden;
background-color: #fff;
padding: 5px;
padding-bottom: 0;
position: relative;
-moz-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.22);
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.22);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.22);
}

.gallery-cover-box{
width: 100%;
padding-top: 60%;
text-align: center;
overflow: hidden;
position: relative;
background: center center no-repeat;
-webkit-background-size: cover;
background-size: cover;
}

.gallery-cover-box .gallery-cover-img {
display: inline-block;
width: 100%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
.gallery-item .gallery-name{
font-size: 14px;
line-height: 24px;
text-align: center;
color: #666;
margin: 5px;
}

.waterfall {
column-count: 4;
column-gap: 1em;
}
.photo-wrapper{
padding-top: 20px;
}
.photo-item {
display: block;
padding-bottom: 0px;
margin-bottom: 14px;
font-size: 0;
-moz-page-break-inside: avoid;
-webkit-column-break-inside: avoid;
break-inside: avoid;
background: white;
-moz-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.22);
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.22);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.22);
}
.photo-item img {
width: 100%;
}
.photo-item .photo-name{
font-size: 14px;
line-height: 30px;
text-align: center;
margin-top: 10px;
margin-bottom: 10px;
border-top: 1px solid #dddddd;
}

.gallery-header{
margin: 25px;
margin-bottom: 0;
padding-top: 35px;
background-color: #fff;
padding-bottom: 20px;
opacity: 0.95;
border-radius: 10px;
}

/*适配移动端布局*/
@media only screen and (max-width: 601px) {
.waterfall {
column-count: 2;
column-gap: 1em;
}
}

建立相册目录和模板

  • 创建相册目录/blog/source/photos
$ hexo new page photos
index.md
1
2
3
4
5
6
---
title: photos
date: 2020-03-01 22:43:35
layout: "photos"
comments: false
---
  • 创建一级相册模板/blog/themes/next/layout/photos.swig
photos.swig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% extends '_layout.swig' %}
{% import '_macro/post-collapse.swig' as post_template with context %}
{% import '_macro/sidebar.swig' as sidebar_template with context %}

{% block content %}
<div class="posts-expand">
<div class="gallery-header" lang="{{ page.lang or page.language or config.language }}">
{% include '_partials/page/page-header.swig' %}
</div>
<div class="gallery-wrapper">
{% for galleries in site.data.galleries %}
<div class="gallery-box">
<a href="./{{ galleries.name }}" class="gallery-item" data-aos="zoom-in-up">
<div class="gallery-cover-box" style="background-image: url({{theme.photos_info.cos_url}}/{{ galleries.name }}/{{ galleries.cover }})">
</div>
<p class="gallery-name">
{{ galleries.name }}
</p>
</a>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

{% block sidebar %}
{{ sidebar_template.render(true) }}
{% endblock %}
  • 创建二级相册模板/blog/themes/next/layout/photo.swig
photo.swig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<meta charset='utf-8'>
<link rel="stylesheet" href="/css/gallery.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/fancybox@3.0.1/dist/css/jquery.fancybox.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/css/justifiedGallery.min.css">

{% extends '_layout.swig' %}
{% import '_macro/post-collapse.swig' as post_template with context %}
{% import '_macro/sidebar.swig' as sidebar_template with context %}

{% block content %}
<div class="posts-expand">
<div class="post-block" lang="{{ page.lang or page.language or config.language }}">
{% include '_partials/page/page-header.swig' %}
<div class="container">
<div class="photo-wrapper">
<div class="waterfall" id="mygallery">
{% for i in range(site.data.galleries.length) %}
{% if site.data.galleries[i].name == page.title %}
{% for photo in site.data.galleries[i].photos %}
<a class="photo-item" rel="example_group" data-fancybox="images" href="{{theme.photos_info.cos_url}}/{{page.title}}/{{photo}}" ><img alt="{{photo}}" src="{{theme.photos_info.cos_url}}/{{page.title}}/{{photo}}" /> </a>
{%- endfor %}
{% endif %}
{%- endfor %}

</div>
</div>
</div>
</div>
</div>
{% endblock %}

{% block sidebar %}
{{ sidebar_template.render(true) }}
{% endblock %}


<script src="https://cdn.jsdelivr.net/npm/fancybox@3.0.1/dist/js/jquery.fancybox.js"></script>
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>
<script>

$("a[rel=example_group]").fancybox();
$("#mygallery").justifiedGallery({margins: 5, rowHeight: 100});

</script>

下载图片数据并创建相应二级相册目录

  首先在腾讯COS创建两级相册目录,并上传图片。再运行/blog/source/photos/blog_cos.py文件,会自动将下载的图片数据存放到 galleries.yaml 文件中,并根据腾讯COS相册文件结构创建对应的的二级相册目录。

blog_cos.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# -*- coding=utf-8
from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
from qcloud_cos import CosServiceError
from qcloud_cos import CosClientError

import os
import sys
import logging
import shutil

# 腾讯云COSV5Python SDK, 目前可以支持Python2.6与Python2.7以及Python3.x
# pip安装指南:pip install -U cos-python-sdk-v5
# cos最新可用地域,参照https://www.qcloud.com/document/product/436/6224

logging.basicConfig(level=logging.INFO, stream=sys.stdout)

# 设置用户属性, 包括secret_id, secret_key, region
# appid已在配置中移除,请在参数Bucket中带上appid。Bucket由bucketname-appid组成
secret_id = '*******************' # 替换为用户的secret_id
secret_key = '*******************' # 替换为用户的secret_key
region = 'ap-chengdu' # 替换为用户的region
token = None # 使用临时密钥需要传入Token,默认为空,可不填
config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token) # 获取配置对象
client = CosS3Client(config)
print("已连接腾讯COS")


galleries = []
json_name = "galleries.yaml" # 图片数据文件名称
bucket_name = "hexo-1257031621" # 腾讯COS存储桶名称
photos_name = "galleries/" # 腾讯COS一级相册目录

# json文件保存路径 例:D://blog/source/_data/galleries.json
cwd = os.getcwd().replace("photos", "_data")
json_cwd = os.path.join(cwd, json_name).replace('\\', '/').replace(':', ':/')

# 查看 json 文件是否已存在
def json_file_exists():
if os.path.exists(json_cwd):
os.remove(json_cwd)

# 获取相册数据信息
def get_galleries_data():
global galleries_name
galleries_name = []
# 查询相册目录下所有内容
response = client.list_objects(
Bucket= bucket_name,
Prefix= photos_name,
Delimiter='/',
)
CommonPrefixes = response['CommonPrefixes']
for Prefixes in CommonPrefixes:
Prefix = Prefixes['Prefix']
gallery_name = Prefix.split("/")[1]
galleries_name.append(gallery_name)
# 查询相册目录下所有子文件夹内容
response = client.list_objects(
Bucket= bucket_name,
Prefix= photos_name + gallery_name + '/',
Delimiter='/',
)
contents = response['Contents']
# 子文件夹所有照片添加到 photos_list 列表
photos_list = []
for i in range(len(contents)):
content = contents[i]['Key'].split("/")[2]
photos_list.append(content)
del photos_list[0]
# 相册添加到 gallery_dict 字典中
gallery_dict = dict(name=1, cover=1, description=1, photos=1)
gallery_dict.update(name = gallery_name)
gallery_dict.update(cover = photos_list[0])
gallery_dict.update(photos = photos_list[0:])
photos_list.clear()
# 所有相册添加到 galleries 列表中
galleries.append(gallery_dict)
print("生成相册数据完成")

# 数据写入
def galleries_data_write():
with open(json_cwd, 'w', encoding='utf-8') as file:
# 修整数据格式
data0 = str(galleries).replace("}, {" , "},\n {")
data1 = data0.replace("[{", "[\n {")
data = data1.replace("}]", "}\n]")
file.write(data)
print("数据写入完成")

# 创建文件夹
def mkdir_galleries(file):
if os.path.exists(file):
shutil.rmtree(file)
os.mkdir(file)

# # 创建二级相册目录和对应的index.md文件
def galleries_index():
for name in galleries_name:
gallery_index_dict= dict(title = name, layout = 'photo', comments = "false")
mkdir_galleries(name)
gallery_index_cwd = os.path.join(os.getcwd(), name , "index.md").replace('\\', '/').replace(':', ':/')
with open(gallery_index_cwd, 'w', encoding='utf-8') as file:
data0 = str(gallery_index_dict)
data1 = data0.replace("{", "---\n").replace(",","\n").replace("}", "\n---")
data = data1.replace("'", "").replace("photo",'''"photo"''').replace(" layout","layout").replace(" comments","comments")
file.write(data)
print("index数据写入完成")

if __name__ == "__main__":
json_file_exists() # 查看 json 文件是否已存在
get_galleries_data() # 获取相册数据信息
galleries_data_write() # 图像数据写入
galleries_index() # 创建二级相册目录和对应的index.md文件
  1. blog_cos.py必须放在/blog/source/photos/文件夹下。
  2. 运行blog_cos.py前需要安装 pip install -U cos-python-sdk-v5
  3. 设置用户属性, 包括secret_id, secret_key, region
  4. 可修改腾讯COS存储桶名称bucket_name、腾讯COS一级相册目录photos_name、图片数据文件名称json_name

腾讯COS跨域访问cors设置

在基础配置中找到cors设置

操作选择GET,其他默认都填成*,如下图:

一般情况下默认是共有读私有写,policy权限就不要设置了

+