给hexo静态博客添加动态相册功能

小鸡
阅读1069 喜欢11 教程 更新2019-5-17

兰州小红鸡

昨天突发奇想给博客写了个相册页面,使用腾讯云cos作为相册的存储桶,使用api在线获取相册里面的存储桶里的照片并且实时生成相册内容。之前也有看过一些人做的相册页面,但是对于我来说,还是感觉不方便。网上的大多是在本地项目文件夹存放照片,然后更改一系列的主题文件来实现相册页面。

比如这位制作的Hexo NexT 博客增加瀑布流相册页面,然而他做的过程已经算是比较简洁了,没有改动太多主题配置。但是这种相册,每次添加新照片的时候,还是需要手动在相册页面添加相应的图片链接与代码。

所以我想能不能做一个直接后台上传图片,不用再改动代码的静态博客的相册页面呢,就像一个动态博客一样,或者像qq相册那样,只需要上传照片就可以了。

答案是可以的,机智的我使用了腾讯云的cos存储桶作为相册后台,调用cos存储桶的xml文件api在线获取图片链接,再使用JavaScript代码动态生成相册内容。

兰州小红鸡的博客相册

效果如下

前方轻微秀恩爱预警

2019-05-07更新
之前写的这篇教程没想到有这么多朋友采纳
不过教程也确实有很多问题
之前写的比较粗糙,让朋友们遇到各种麻烦
现在更新一下
写一下我能想到的各种bug的解决方法

步骤如下:

创建腾讯云cos存储桶

这个比较简单,搜索腾讯云,注册账号登陆,在云产品中选择对象存储,新建一个存储桶。就OK了。

兰州小红鸡

跨域访问cors设置

在基础配置中找到cors设置

兰州小红鸡

操作选择GET,来源Origin填写你的域名,带http或者https,其他默认不要填,如下图

兰州小红鸡

注意:如果填了域名还是遇到跨域问题,那么就把origin源填为*,比如下面这样(其实刚开始弄的话建议都填成*好了)

读写权限

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

访问域名

然后记住这个地方访问域名,这里就是我们动态生成相册,获取链接时需要用到的xml链接,下面要用到

兰州小红鸡

复制这个访问域名,看能不能在浏览器中打开,如果可以打开并且没有显示error节点,那么就可以继续下面的操作,否则查看上面哪一步出错并进行改正

上传照片

上传照片方式有很多(推荐用coscmd命令行写个脚本),不过这里教程中你就直接上腾讯云后台上传就好了,想要其他骚操作等相册做好了再自己百度吧。

重要事项

  1. 上传照片前,先在存储桶中建立一个文件夹,也就是你的相册名字,当然你也可以新建多个文件夹。
  2. 但是有一点需要需要注意的是,不能直接上传一个文件夹,那样会出bug,见完文件夹后往里面上传照片,文件夹里面不能再新建文件夹了(除非你自己改造下面的相应代码)
  3. 每个文件中需要一张命名为封面的图片,它会作为你的该文件夹相册的封面

然后就ok了

hexo本地配置

在本地项目新建一个相册页面

$ hexo new page photos

19-01-15更新:将照片地展示分不同相册加载

编辑sourcephotos路径下的index.md文件,写入以下代码

记得在下面的代码中填写xmllink的值,也就是上面提到的你的存储桶访问域名

<style type="text/css">
.main-inner{
width: 100%;
}
.main {
padding-bottom: 150px;
margin-top: 0px;
background: #121212;
}
.main-inner{
margin-top: unset;
}
.page-post-detail .post-meta{
display: none;
}
body {
background-image: unset;
background-attachment: unset;
background-size: 100%;
/*background-position: top left;*/
}
.header{
background: rgba(28, 25, 25, 0.6);
border-bottom: unset;
}
.menu .menu-item a{
font-weight: 300;
color: #e6eaed;
}

.imgbox{
width: 100%;
overflow: hidden;
height: 250px;
border-right: 1px solid #bcbcbc;
}
.box{
visibility: visible;
overflow: auto;
zoom: 1;
}
.box li{
float: left;
width: 25%;
position: relative;
overflow: hidden;
text-align: center;
list-style: none;
margin: 0;
/*display: inline;*/
padding: 0;
height: 360px;
}
.box li span{
display: block;
padding: 4% 7% 10% 7%;
min-height: 80px;
background: #fff;
color: #fff;
font-size: 16px;
background: #121212;
font-weight: 600;
line-height: 26px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}

img.imgitem{
padding: unset;
padding: unset;
border: unset;
position: relative;
padding: 0px;
height: auto;
width: 100%;
}

.comments {
margin: auto !important;

width: 60%;

}

div#posts.posts-expand {
border: unset;
padding: unset;
margin-bottom: 10px;
}
.posts-expand .post-body img{
padding: 12px !important;
}
.box p{
display: block;
background: #121212;
color: #fff;
font-size: 12px;
font-family: "SwisMedium";
-webkit-box-sizing: border-box;
box-sizing: border-box;
text-align: center;
}

.box span strong{
background: rgba(0,0,0,0.4);
padding: 20px;
}

.posts-expand .post-title {
display: none;
}
.title{
display: inline-block;
vertical-align: middle;
background: url(https://image.idealli.com/bg11.jpg);
font: 85px/250px "ChaletComprimeMilanSixty";
background-position: left bottom !important;
color: #fff;
background-size: 100% auto !important;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
width: 100%;
text-align: center;
border: unset;
height: 700px;
cursor: unset !important;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.btn-more-posts{
display: inline-block;
vertical-align: middle;
font: 85px/250px "ChaletComprimeMilanSixty";
color: #000;
width: 100%;
text-align: center;
border: unset;
height: 400px;
background-color: #121212;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}

@media (max-width: 767px){
.box li {
width: 100%;
}
.title {
height: 200px;
}

.posts-expand .post-body img{
padding: 0px !important;
}

.box span {
min-height: 80px;
border-right: unset;
font-size: 17px;
}
.box p{
border-right: unset;
font-size: 12px;

}
.posts-expand {
margin: unset;
}
.comments {
width: 96%;
}


}

@media (min-width: 1600px){
.container .main-inner{
width: 100%;
}
}

.footer{
background-color: #121212 !important;
}
.v * {
color: #f4f4f4 !important;
}

.v .vwrap .vmark .valert .vcode {
background: #00050b !important;
}



</style>

<div id="box" class="box"></div>

<img src="https://me.idealli.com/images/load.gif" data-src="https://image.idealli.com/bg12.jpg" style="width: 100%;height: auto;">

<script type="text/javascript">
document.getElementsByTagName("img")[0].src="https://me.idealli.com/images/load.gif" data-src="/images/photoico.png";

function loadXMLDoc(xmlUrl)
{
try //Internet Explorer
{
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
}
catch(e)
{
try //Firefox, Mozilla, Opera, etc.
{
xmlDoc=document.implementation.createDocument("","",null);
}
catch(e) {alert(e.message)}
}

try
{
xmlDoc.async=false;
xmlDoc.load(xmlUrl);
}
catch(e) {
try //Google Chrome
{
var chromeXml = new XMLHttpRequest();
chromeXml.open("GET", xmlUrl, false);
chromeXml.send(null);
xmlDoc = chromeXml.responseXML.documentElement;
//alert(xmlDoc.childNodes[0].nodeName);
//return xmlDoc;
}
catch(e)
{
alert(e.message)
}
}
return xmlDoc;
}

// if (window.XMLHttpRequest)
// {// code for IE7+, Firefox, Chrome, Opera, Safari
// xmlhttp=new XMLHttpRequest();
// }
// else
// {// code for IE6, IE5
// xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
// }
// xmlhttp.open("GET","https://images-1256429518.cos.ap-chengdu.myqcloud.com",false);
// xmlhttp.send();
xmlDoc=loadXMLDoc("https://images-1256429518.cos.ap-chengdu.myqcloud.com");

var urls=xmlDoc.getElementsByTagName("Key");
var date=xmlDoc.getElementsByTagName("LastModified");
var wid=250;
var showNum=12; //每个相册一次展示多少照片
if ((window.innerWidth)>1200) {wid=(window.innerWidth*3)/18;}
var box=document.getElementById("box");
var i=0;

var content=new Array();
var tmp=0;
var kkk=-1;
for (var t = 0; t < urls.length ; t++) {
var bucket=urls[t].innerHTML;
var length=bucket.indexOf("/");
if(length===bucket.length-1){
kkk++;
content[kkk]=new Array();
content[kkk][0]={"url":bucket,"date":date[t].innerHTML.substring(0,10)};
tmp=1;
}
else {
content[kkk][tmp++]={"url":bucket.substring(length+1),"date":date[t].innerHTML.substring(0,10)};
}
}

for (var i = 0; i < content.length; i++) {
var conBox=document.createElement("div");
conBox.id="conBox"+i;
box.appendChild(conBox);
var item=document.createElement("div");
var title=content[i][0].url;
item.innerHTML="<button class=title style=background:url(https://photos.idealli.com/"+title+"封面.jpg"+");><span style=display:inline;><strong style=color:#f0f3f6; >"+title.substring(0,title.length-1)+"</strong></span></button>";
conBox.appendChild(item);

for (var j = 1; j < content[i].length && j < showNum+1; j++) {
var con=content[i][j].url;
var item=document.createElement("li");
item.innerHTML="<div class=imgbox id=imgbox style=height:"+wid+"px;><img class=imgitem src="https://me.idealli.com/images/load.gif" data-src=https://photos.idealli.com/"+title+con+" alt="+con+"></div><span>"+con.substring(0,con.length-4)+"</span><p>上传于"+content[i][j].date+"</p>";
conBox.appendChild(item);
}
if(content[i].length > showNum){
var moreItem=document.createElement("button");
moreItem.className="btn-more-posts";
moreItem.id="more"+i;
moreItem.value=showNum+1;
let cur=i;
moreItem.onclick= function (){
moreClick(this,cur,content[cur],content[cur][0].url);
}
moreItem.innerHTML="<span style=display:inline;><strong style=color:#f0f3f6;>加载更多</strong></span>";
conBox.appendChild(moreItem);
}
}

function moreClick(obj,cur,cont,title){
var parent=obj.parentNode;
parent.removeChild(obj);
var j=obj.value;
var begin=j;
for ( ; j < cont.length && j < Number(showNum) + Number(begin); j++) {
var con=cont[j].url;
var item=document.createElement("li");
item.innerHTML="<div class=imgbox id=imgbox style=height:"+wid+"px;><img class=imgitem src="https://me.idealli.com/images/load.gif" data-src=https://photos.idealli.com/"+title+con+" alt="+con+"></div><span>"+con.substring(0,con.length-4)+"</span><p>上传于"+cont[j].date+"</p>";
parent.appendChild(item);
var v=item.getElementsByTagName("img");
v[0].id=imgid;
imgid++;
v[0].addEventListener("click",function(e){ // 注册事件
// 记录小图的位置个大小
x=e.target.offsetLeft;
y=e.target.offsetTop;
w=e.target.offsetWidth;
h=e.target.offsetHeight;
src="https://me.idealli.com/images/load.gif" data-src=e.target.src;
id=e.target.id;
// 创建遮罩层
div=document.createElement("div");
div.style.cssText=`
position:fixed;
left:0;
top:0;
bottom:0;
right:0;
background-color: rgba(25,25,25,0.8);
z-index:99999999;
transition:all .3s cubic-bezier(0.165, 0.84, 0.44, 1);
`;
document.body.appendChild(div);
setTimeout(function(){
div.style.opacity=1;
},0);
// (此处可以加loading)

// 创建副本
img=new Image();
btnright=document.createElement("button");
btnleft=document.createElement("button");
img.src="https://me.idealli.com/images/load.gif" data-src=src;
btnleft.style.cssText=`
position:fixed;
border-radius: 50%;;
left:${x - 20}px;
top:${y - container.scrollTop + h/2}px;
width:50px;
height:50px;
border: 0px;
background-color: rgba(200,200,200,0.8);
font-size: 20px;
z-index: 999999999;
transition:all .3s cubic-bezier(0.165, 0.84, 0.44, 1);
`;
btnright.style.cssText=`
position:fixed;
border-radius: 50%;
left:${x + w + 20}px;
top:${y - container.scrollTop + h/2}px;
width:50px;
border: 0px;
height:50px;
font-size: 20px;
background-color: rgba(200,200,200,0.8);
z-index: 999999999;
transition:all .3s cubic-bezier(0.165, 0.84, 0.44, 1);
`;
btnleft.innerText="<";
btnright.innerText=">";

img.style.cssText=`
position:fixed;
border-radius: 12px;
left:${x}px;
top:${y - container.scrollTop}px;
width:${w}px;
height:${h}px;
z-index: 999999999;
transition:all .3s cubic-bezier(0.165, 0.84, 0.44, 1);
opacity:0;
`;

btnleft.onclick=function(){
if(id===0){
alert("已经是第一张了!");
return;
}
var left=document.getElementById(id-1);
img.src="https://me.idealli.com/images/load.gif" data-src=left.src;
x=left.offsetLeft;
y=left.offsetTop;
w=left.offsetWidth;
h=left.offsetHeight;
id--;
}
btnright.onclick=function(){
id++;
if(id>=imgid){
alert("已经是最后一张了!");
return;
}
var right=document.getElementById(id);
img.src="https://me.idealli.com/images/load.gif" data-src=right.src;
x=right.offsetLeft;
y=right.offsetTop;
w=right.offsetWidth;
h=right.offsetHeight;
}
img.onload=function(){
document.body.appendChild(img);
document.body.appendChild(btnright);
document.body.appendChild(btnleft);

// 浏览器宽高
wh=window.innerHeight;
ww=window.innerWidth;

// 目标宽高和坐标
if(w/h<ww/wh){
th=wh-80;
tw=w/h*th >> 0;
tx=(ww - tw) / 2;
ty=40;
}
else{
tw=ww*0.8;
th=h/w*tw >> 0;
tx=ww*0.1;
ty=(wh-th)/2;
}

// 延迟写入否则不会有动画
setTimeout(function(){
img.style.opacity=1;
img.style.height=th+"px";
img.style.width=tw+"px";
img.style.left=tx+"px";
img.style.top=ty+"px";
btnleft.style.left=(tx-90)+"px";
btnleft.style.top=(ty+th/2)+"px";
btnright.style.left=(tx+tw+40)+"px";
btnright.style.top=(ty+th/2)+"px";
// 点击隐藏
div.onclick=img.onclick=closeMove;
},10);
};
});//end event
}
if(cont.length > j){
obj.value=j;
parent.appendChild(obj);
}
}


</script>


然后刷新hexo渲染

$ hexo clean
$ hexo d -g

再往cos存储桶里上传照片,就可以了!效果如下
重要的是以后更新照片都不用改动代码。往腾讯云cos上传照片就好了,而且腾讯云页提供了很多工具可以再本地命令行上传照片,非常方便,感兴趣可以自行百度。

兰州小红鸡

嘻嘻女朋友真可爱!

兰州小红鸡

自己写界面样式

注意

以上教程是基于我自己的博客主题(next主题Mist样式)做的适配
所以可能会出现一些样式问题
强烈建议会前端的朋友自己写样式和逻辑代码,确保不会出错。
不会前端的朋友也可以学一学css自己改改,挺有趣的
另外都在搞这个了,应该也都会一点了
自己写css样式也不麻烦,一个小时就能把相册改造成自己喜欢的风格

代码核心

上面的photo界面的核心代码是获取腾讯云xml文件的那段

function loadXMLDoc(xmlUrl) 
{
try //Internet Explorer
{
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
}
catch(e)
{
try //Firefox, Mozilla, Opera, etc.
{
xmlDoc=document.implementation.createDocument("","",null);
}
catch(e) {alert(e.message)}
}

try
{
xmlDoc.async=false;
xmlDoc.load(xmlUrl);
}
catch(e) {
try //Google Chrome
{
var chromeXml = new XMLHttpRequest();
chromeXml.open("GET", xmlUrl, false);
chromeXml.send(null);
xmlDoc = chromeXml.responseXML.documentElement;
//alert(xmlDoc.childNodes[0].nodeName);
//return xmlDoc;
}
catch(e)
{
alert(e.message)
}
}
return xmlDoc;
}

var xmllink="你的访问域名链接"
//访问域名链接就是我上面提到的那个访问域名xml链接

xmlDoc=loadXMLDoc(xmllink);
var urls=xmlDoc.getElementsByTagName(Key);

urls便是获取了相册的所有链接,之后要做的事情就是用js动态生成img元素
我的是这样写的,当然你也可以使用别的写法,写适合自己风格的相册样式

var urls=xmlDoc.getElementsByTagName(Key);
var date=xmlDoc.getElementsByTagName(LastModified);
var wid=(window.innerWidth*3)/18;
var box=document.getElementById(box);
var i=0;

for ( ; i < 21 && i<urls.length; i++) {
var bucket=urls[i].innerHTML;
var length=bucket.indexOf(/);
if(length===bucket.length-1){
var item=document.createElement("div");
item.innerHTML="<button class=btn-more-posts><span style=display:inline;><strong style=color:#f0f3f6; >"+bucket.substring(0,length)+"</strong></span></button>";
box.appendChild(item);
continue;
}

var item=document.createElement("li");
item.innerHTML="<div class=imgbox id=imgbox style=height:"+wid+"px;><img class=imgitem src="+xmllink+/+bucket+" ></div><span>"+bucket+"</span><p>"+date[i].innerHTML.substring(0,10)+"</p>";
box.appendChild(item);

}

var morep=document.getElementById(more);
morep.onclick=function(){
var temp=i+20;

for ( ; i < temp && i<urls.length; i++) {
var bucket=urls[i].innerHTML;
var length=bucket.indexOf(/);
if(length===bucket.length-1){
var item=document.createElement("div");
item.innerHTML="<button class=btn-more-posts><span style=display:inline;><strong style=color:#f0f3f6; >"+bucket.substring(0,length)+"</strong></span></button>";
box.appendChild(item);
continue;
}
var item=document.createElement("li");
item.innerHTML="<div class=imgbox id=imgbox style=height:"+wid+"px;><img class=imgitem src="+xmllink+/+urls[i].innerHTML+" ></div><span>"+urls[i].innerHTML+"</span><p>"+date[i].innerHTML.substring(0,10)+"</p>";
box.appendChild(item);

}
}

整个教程还是提供了一个思路,如果遇到问题了,请根据自己的实际情况进行
我的博客主题:Next主题Mist样式

注意事项

  1. 上传照片前,先在存储桶中建立一个文件夹,也就是你的相册名字,当然你也可以新建多个文件夹。
  2. 但是有一点需要需要注意的是,不能直接上传一个文件夹,那样会出bug,见完文件夹后往里面上传照片,文件夹里面不能再新建文件夹了(除非你自己改造下面的相应代码)
  3. 每个文件中需要一张命名为封面的图片,它会作为你的该文件夹相册的封面
  4. 确保存储桶的xml域名能在浏览器上访问
  5. 暂时不要设置policy权限啥的,等你相册制作完了,如果怕照片被盗可以添加这些东西
  6. 尽量自己修改样式,不然肯定不好看(除非你的主题和我的一样)
  7. css样式直接写在md文件中,不用写在全局里

下面是一些我的建站笔记汇总,平常做的小手工,希望对大家有帮助

hexo博客搭建以及next美化教程 原生js实现网页图片点击展示效果 用回valine评论系统,valine评论框样式美化 给hexo静态博客添加动态相册功能 hexo建站笔记之首页文章轮播图 模仿知乎的链接卡片 开始使用腾讯云图床 将公众号文章爬到hexo博客 使用腾讯云cdn加速博客 hexo建站笔记之彩色标签云