博客:https://www.mintimate.cn Mintimate’s Blog,只为与你分享
短链接,相信大家再熟悉不过了。比如之前写过的文章:
原理就是:
用短链接替换较长的原始 URL,使得用户在访问网页或资源时可以使用更短、更便于记忆和分享的链接,也方便隐藏Get请求。
但是,这样的短链接,还是缺少一些乐趣。从算法和乐趣触发,长链接,了解一下?
其实并没有公认的长链接定义,我之所以称本次内容为长链接生成,是因为本次介绍的算法效果,和短链接最后达成的效果相反。
这一切源自于我看到这个网站:
这个网站看起来全都是o
,但是你细看再细看,就会发现,实际上包含4四种不同的字符:
o
是英语小写字母oο
是希腊字母omicronо
是西里尔字母оᴏ
是小号形式的字母o当然,更有趣的是,这个网站所做的内容:
举个例子:你输入网址:https://cloud.tencent.com/developer,那么可以得到:
访问得到的字符串,发现实际上重定向到了:https://cloud.tencent.com/developer。
也就是把一个URL链接,变长和风格化了。
那么,是怎么做到的呢? 又是如何复现呢?
嘿嘿,本来想F12看看是不是前端实现的,结果发现作者直接开源了这个好玩的网站:
核心代码就是这块:
这个时候,我们就知道原理了:
简单地说,访问访问这个网站,如果存在二级目录,那么:
为什么使用UTF-8数组进行字段的映射呢?
首先,我们要知道UTF-8是Unicode的一种字节序列表示形式(编码方案),UTF-8将一个Unicode字符根据其码点转化为1-4个字节的序列来存储和传输。
基础的Unicode定义了从0到1114111之间的码位空间,用于表示世界上主流文字系统中的字符。
例如:
A
的Unicode码点是0x0041
,数字0
的码点是0x0030
。回到UTF-8,因为UTF-8为1-4个字节的序列,所以可以用UTF-8数组来表示,比如你好世界
:
[228, 189, 160]
[229, 165, 189]
所以,"你好世界"每个字符的UTF-8编码数组是:
[228, 189, 160, 229, 165, 189, 224, 168, 104, 227, 174, 164]
根据用例,转换过程就是:
这样就完成了从字符串到UTF-8编码数组的转换。并且,新的数组一定是1~4个数字序列,每个字符序列,由高到低排序。
综合上述的UTF-8数组,我们可以把任意的字符全部转为UTF-8的数组,并且数据内部全部是数组。
这个时候,是怎么映射为o
呢?
关键代码:
enc = ["o", "ο", "о", "ᴏ"]
encodeUrl(url) {
// get utf8 array
let unversioned = this.toUTF8Array(url)
// convert to string with base 4
// padstart very important! otherwise missing leading 0s
.map(n => n.toString(4).padStart(4, "0"))
// convert to array of characters
.join("").split("")
// map to the o's
.map(x => this.enc[parseInt(x)])
// join into single string
.join("")
return this.addVersion(unversioned)
}
核心逻辑:
o
);这样,原本的你好世界
,就被映射为:ᴏоοoоᴏᴏοооooᴏоοοооοοоᴏᴏο
。
恢复就很简单了,上一节的操作反着进行就可以了:
decodeUrl(ooo) {
ooo = this.removeAndCheckVersion(ooo)
if (ooo === null) return
// get the base 4 string representation of the url
let b4str = ooo.split("").map(x => this.dec[x]).join("")
let utf8arr = []
// parse 4 characters at a time (255 in b10 = 3333 in b4)
// remember adding leading 0s padding
for (let i = 0; i < b4str.length; i += 4)
utf8arr.push(parseInt(b4str.substring(i, i + 4), 4))
return this.Utf8ArrayToStr(utf8arr)
}
可以看到,还是很简单的。就是思路,非常新颖。
掌握了原理,我们就可以复刻为音符的版本了,既然原版使用四个不同的o
,那么我们可以使用特殊符号:"♫", "♪", "♬", "¶","♩"
。
首先是定义一个env,用于映射:
const enc = ["♫", "♪", "♬", "¶","♩"]
既然使用了五个音符,那么就应该用5进制了:
// 获取utf8数组
let UTF8Array = toUTF8Array(originUrl)
// 转换为base 4字符串
// padstart非常重要!否则会丢失前导0
.map(n => n.toString(5).padStart(5, "0"))
// 转换为字符数组
.join("").split("")
// 映射到o的不同形式
.map(x => enc[parseInt(x)])
// 连接成单个字符串
.join("");
解码也需要更改一下:
decodeUrl(ooo) {
ooo = this.removeAndCheckVersion(ooo)
if (ooo === null) return
// 每次解析5个字符
let b5str = ooo.split("").map(x => this.dec[x]).join("")
let utf8arr = []
for (let i = 0; i < b5str.length; i += 5)
utf8arr.push(parseInt(b5str.substring(i, i + 5), 5))
return this.Utf8ArrayToStr(utf8arr)
}
最后,看看效果,输入:
https://cloud.tencent.com/developer
输出:
♫♫♩♫♩♫♫♩¶♪♫♫♩¶♪♫♫♩♬♬♫♫♩¶♫♫♫♬♪¶♫♫♪♩♬♫♫♪♩♬♫♫¶♩♩♫♫♩♪¶♫♫♩♬♪♫♫♩¶♬♫♫♩♫♫♫♫♪♩♪♫♫♩¶♪♫♫♩♫♪♫♫♩♬♫♫♫¶♩♩♫♫♩♫♪♫♫♩♬♫♫♫♩¶♪♫♫♪♩♪♫♫¶♩♩♫♫♩♬♪♫♫♩♪♩♫♫♪♩♬♫♫♩♫♫♫♫♩♫♪♫♫♩¶¶♫♫♩♫♪♫♫♩♪¶♫♫♩♬♪♫♫♩♬♬♫♫♩♫♪♫♫♩♬♩
接下来看看如何网站上实现。
我们需要达成一个302的重定向跳转。在SpringBoot中,你可以使用RedirectView
进行跳转:
@GetMapping("/path")
public View handle() {
return new RedirectView("/new/path", true);
}
如果使用Nginx,可以直接激活Nginx的Lua脚本,使用Lua脚本对字符串进行编码转字符串解析后:
location /old-path {
rewrite ^ /new-path? permanent last;
}
我最近用Nuxt3比较多,就说一下Nuxt3上如何操作。
在Nuxt3上,编码部分就不再多说了,跳转解码,可以使用服务端路由进行实现:
import { Utf8ArrayToStr } from '~/untils/longUrlMake';
export default defineEventHandler((event) => {
const ooo = decodeURIComponent(event.context.params.encoderUrl);
console.log(ooo)
const dec = {
'♫': '0',
'♪': '1',
'♬': '2',
'¶': '3',
'♩': '4'
};
// 获取url的base 5字符串表示
let b5str = ooo
.split('')
.map((x) => dec[x])
.join('');
console.log("b5str: "+b5str)
if (b5str === undefined || b5str.length ===0){
return sendRedirect(event, "/404", 302);
}
let utf8arr = [];
// 每次解析5个字符
// 记住添加前导0的填充
for (let i = 0; i < b5str.length; i += 5)
utf8arr.push(parseInt(b5str.substring(i, i + 5), 5));
// 返回解码后的字符串
let originUrl = Utf8ArrayToStr(utf8arr);
return sendRedirect(event, originUrl, 302);
});
这样,就可以完成映射,并且直接使用302进行跳转。如果存在解析失败,直接跳转到404的界面。
好啦,本次的演示就到这里。或许有小伙伴问,这样把URL变长,有什么用呢?
实际上,确实用处不大,最多也就是隐藏地址内容、隐藏Get请求参数;并且乐趣十足。
不过呢,使用UTF-8数组,确实是一个很精巧的方法,后续其他的算法,也可以进行考虑。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。