为hexo的next主题应用短链功能

简述

原理上本博客内容对所有网站都适用,不过本篇讲述是基于hexo的next主题上的应用。如果你恰好是用hexo搭建博客,且应用得是next主题,那么恭喜你,你可以直接复制下面的代码应用短链服务。否则,你得根据自己网站的需要进行相应的适应性修改。

前期准备

本功能需要用到md5算法,所以首先需要做的是准备一个md5算法的js库。你可以到网上下载,由于md5算法代码不是很长,为了方便,直接贴在下面,你可以复制保存为md5.js备用。

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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/*
* JavaScript MD5 1.0.1
* https://github.com/blueimp/JavaScript-MD5
*
* Copyright 2011, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*
* Based on
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for more info.
*/

/*jslint bitwise: true */
/*global unescape, define */

(function ($) {
'use strict';

/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF),
msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}

/*
* Bitwise rotate a 32-bit number to the left.
*/
function bit_rol(num, cnt) {
return (num << cnt) | (num >>> (32 - cnt));
}

/*
* These functions implement the four basic operations the algorithm uses.
*/
function md5_cmn(q, a, b, x, s, t) {
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
}
function md5_ff(a, b, c, d, x, s, t) {
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t) {
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t) {
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t) {
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}

/*
* Calculate the MD5 of an array of little-endian words, and a bit length.
*/
function binl_md5(x, len) {
/* append padding */
x[len >> 5] |= 0x80 << (len % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;

var i, olda, oldb, oldc, oldd,
a = 1732584193,
b = -271733879,
c = -1732584194,
d = 271733878;

for (i = 0; i < x.length; i += 16) {
olda = a;
oldb = b;
oldc = c;
oldd = d;

a = md5_ff(a, b, c, d, x[i], 7, -680876936);
d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);

a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i], 20, -373897302);
a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);

a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
d = md5_hh(d, a, b, c, x[i], 11, -358537222);
c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);

a = md5_ii(a, b, c, d, x[i], 6, -198630844);
d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);

a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
}
return [a, b, c, d];
}

/*
* Convert an array of little-endian words to a string
*/
function binl2rstr(input) {
var i,
output = '';
for (i = 0; i < input.length * 32; i += 8) {
output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
}
return output;
}

/*
* Convert a raw string to an array of little-endian words
* Characters >255 have their high-byte silently ignored.
*/
function rstr2binl(input) {
var i,
output = [];
output[(input.length >> 2) - 1] = undefined;
for (i = 0; i < output.length; i += 1) {
output[i] = 0;
}
for (i = 0; i < input.length * 8; i += 8) {
output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
}
return output;
}

/*
* Calculate the MD5 of a raw string
*/
function rstr_md5(s) {
return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
}

/*
* Calculate the HMAC-MD5, of a key and some data (raw strings)
*/
function rstr_hmac_md5(key, data) {
var i,
bkey = rstr2binl(key),
ipad = [],
opad = [],
hash;
ipad[15] = opad[15] = undefined;
if (bkey.length > 16) {
bkey = binl_md5(bkey, key.length * 8);
}
for (i = 0; i < 16; i += 1) {
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
}

/*
* Convert a raw string to a hex string
*/
function rstr2hex(input) {
var hex_tab = '0123456789abcdef',
output = '',
x,
i;
for (i = 0; i < input.length; i += 1) {
x = input.charCodeAt(i);
output += hex_tab.charAt((x >>> 4) & 0x0F) +
hex_tab.charAt(x & 0x0F);
}
return output;
}

/*
* Encode a string as utf-8
*/
function str2rstr_utf8(input) {
return unescape(encodeURIComponent(input));
}

/*
* Take string arguments and return either raw or hex encoded strings
*/
function raw_md5(s) {
return rstr_md5(str2rstr_utf8(s));
}
function hex_md5(s) {
return rstr2hex(raw_md5(s));
}
function raw_hmac_md5(k, d) {
return rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d));
}
function hex_hmac_md5(k, d) {
return rstr2hex(raw_hmac_md5(k, d));
}

function md5(string, key, raw) {
if (!key) {
if (!raw) {
return hex_md5(string);
}
return raw_md5(string);
}
if (!raw) {
return hex_hmac_md5(key, string);
}
return raw_hmac_md5(key, string);
}

if (typeof define === 'function' && define.amd) {
define(function () {
return md5;
});
} else {
$.md5 = md5;
}
}(this));

基本配置

  • 将md5.js复制到next主题下的source/js/src目录下
  • 打开layout_scripts\commons.swig,给js_commons数组开头插入一个字符串:’src/md5.js’,请记住一定要在第一个,因为稍后我们需要在source/js/src/utils.js中编写代码,md5.js必须在之前加载好。添加后文件内容如下:
1
2
3
4
5
6
7
8
9
10
11
{%
set js_commons = [
'src/md5.js',
'src/utils.js',
'src/motion.js'
]
%}

{% for common in js_commons %}
<script type="text/javascript" src="{{ url_for(theme.js) }}/{{ common }}?v={{ theme.version }}"></script>
{% endfor %}

生成短链映射数据

在blog根目录(public同级目录)下,新建一个generate.js文件,文件内容如下:

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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
var fs = require('fs');
var md5 = require('./themes/me/source/js/src/md5').md5;

var ROOT_DIR = 'public';
var EXCLUDE = ['/css','/image','/js','/lib','/atom.xml','/favicon.ico','/index.html','/search.xml'];

Array.prototype.indexOf = function(val){
for(var i=0,n=this.length; i<n; i++){
if (this[i] === val){
return i;
}
}
return -1;
}

Array.prototype.startsWith = function(val){
for(var item of this){
if(val.startsWith(item)){
return true;
}
}
return false;
}

//递归遍历文件
var getFileList = function(path,exclude){

var ergodicFile = function(path,relativePath,fileList){

if(!fs.existsSync(path)){
return;
}

if(exclude.startsWith(relativePath)){
return;
}

var files = fs.readdirSync(path);
files.forEach(function(file){

var states = fs.statSync(path + '/' + file);
if(states.isDirectory()){

var obj = {};
obj.type = 'dir';
obj.size = states.size;
obj.name = file;
obj.path = path + '/' + file;
obj.relativePath = relativePath + '/' + file;
fileList.push(obj);

ergodicFile(path + '/' + file,relativePath + '/' + file,fileList);

}
// else{
// var obj = {};
// obj.type = 'file';
// obj.size = states.size;
// obj.name = file;
// obj.path = path + '/' + file;
// obj.relativePath = relativePath + '/' + file;
// fileList.push(obj);

// }

});

};

var fileList = [];

ergodicFile(path,'',fileList);

return fileList;
};

//获取短链
var getLinkShort = function(url){

var key = 'alexis';
var urlhash = md5(key,url);
var len = urlhash.length;
var charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
var shortUrlList = [];

//将加密后的串分成4段,每段4字节,对每段进行计算,一共可以生成四组短连接
for (var i = 0; i < 4; i++) {
var urlhashPiece = urlhash.substr(i * len / 4, len / 4);
//将分段的位与0x3fffffff做位与,0x3fffffff表示二进制数的30个1,即30位以后的加密串都归零
var hex = parseInt(urlhashPiece,16) & 0x3fffffff; //此处需要用到hexdec()将16进制字符串转为10进制数值型,否则运算会不正常

var shortUrl = '';
//生成6位短连接
for (var j = 0; j < 6; j++) {
//将得到的值与0x0000003d,3d为61,即charset的坐标最大值
shortUrl += charset.charAt(hex & 0x0000003d);
//循环完以后将hex右移5位
hex = hex >> 5;
}

shortUrlList.push(shortUrl);
}

return shortUrlList[0];
}

var duang = function(files){
var shortUrl = {};
shortUrl.toLong = {};
shortUrl.toShort = {};

for(var file of files){
var relativePath = file.relativePath.substr(1);
var hash = '#' + getLinkShort(relativePath);

shortUrl.toLong[hash] = relativePath + '/';
shortUrl.toShort[relativePath + '/'] = hash;
shortUrl.toShort[relativePath + '/#more'] = hash;
}

return shortUrl;
}

var fileList = getFileList(ROOT_DIR,EXCLUDE);
var shortUrl = duang(fileList);

fs.writeFile('./public/shortUrl.json',JSON.stringify(shortUrl),{flag:'w',encoding:'utf-8',mode:'0666'},function(err){
if(err){
console.log("文件写入失败")
}else{
console.log("文件写入成功");
}
});

添加短链功能

打开主题下source/js/src/utils.js文件,在最后面添加如下代码:

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

me = {};
NexT.me = me;

if (window.history && window.history.pushState){
me.shortUrl = null;
//此处配置基网址
me.base = 'http://blog.aoshiguchen.com/';

//方便本地测试
if(document.domain == 'localhost'){
me.base = 'http://localhost:4000/';
}

$.ajax({
type: "get",
url: "/shortUrl.json",
cache:false,
async:false,
dataType: "json",

success: function(data){
me.shortUrl = data;
}
});

console.log('shortUrl',me.shortUrl);

var shortMap = me.shortUrl.toLong;
var longMap = me.shortUrl.toShort;

if(document.location.href != me.base){
var url = document.location.href.substr(me.base.length);
//如果是短链访问,则跳转到长链
if(shortMap[url]){
window.location.href = me.base + shortMap[url];
}else if(longMap[decodeURI(url)]){
//如果是长链,则生成对应的短链,并记录到短链、长链映射
//然后将地址栏回显为短链
var shortUrl = longMap[decodeURI(url)];

history.pushState(null,null,'../../../../../../../../../../../../../' + shortUrl);
}
}

}

使用方法

在hexo g命令生成静态博客之后,在blog根目录下执行以下命令,执行后会在public目录下生成一个shortUrl.json文件,然后发布即可。

1
node generater

总结

如果你按照上面的配置完,恭喜你成功应用上了短链功能。但目前加上这个功能,却留下了一个bug,每次点击浏览器后退时,都需要点击两次才能回到上一页面。有好的解决方案欢迎在下面留言^_^。

你的鼓励,是我前进的动力!