因为自己的这个博客没有依赖数据库,所有的文章都是实时读取的md文件,因此灵活的分类就会导致路径名称过长或者是一串中文,如: /article?path=%2f后端开发%2fGOLANG%2f前端自动部署工具MareWood开源.md 某些地方还会转码为 %2f%e5%89%8d%e7%ab%af%e5%bc%80%e5%8f%91%2fREACT%e5%92%8cVUE%e7%9a%84%e5%a4%9a%e5%a5%97%e6%89%93%e5%8c%85%e7%8e%af%e5%a2%83.md 看起来不仅不美观还长,因此需要做成短链的形式。
目前短链有两种算法,自增ID法和摘要算法
自增ID
设置 id 自增,一个 10进制 id 对应一个 62进制的数值,1对1,也就不会出现重复的情况。这个利用的就是低进制转化为高进制时,字符数会减少的特性,短址的长度一般设为 6 位,而每一位是由 [a - z, A - Z, 0 - 9] 总共 62 个字母组成的,所以 6 位的话,总共会有 62^6 ~= 568亿种组合,基本上够用了。
摘要算法
- 将长网址 md5 生成 32 位签名串,分为 4 段, 每段 8 个字节
- 对这四段循环处理, 取 8 个字节, 将他看成 16 进制串与 0x3fffffff(30位1) 与操作, 即超过 30 位的忽略处理
- 这 30 位分成 6 段, 每 5 位的数字作为字母表的索引取得特定字符, 依次进行获得 6 位字符串
- 总的 md5 串可以获得 4 个 6 位串,取里面的任意一个就可作为这个长 url 的短 url 地址
这种算法,虽然会生成4个,但是仍然存在重复几率
自增ID一般需要数据库的自增ID配合,因此我们选择摘要算法,当然,对于一个博客来说,根本不需要考虑重复的问题,这才几百个路径,互联网那么多url都不怕。
package short
import (
"crypto/md5"
"fmt"
"strconv"
)
const charset = "A0a12B3b4CDc56Ede7FGf8Hg9IhJKiLjkMNlmOPnQRopqrSstTuvUVwxWXyYzZ"
func generateCharset(url, hexMd5 string, len, sectionNum int, cb func(url, keyword string) bool) string {
for i := 0; i < sectionNum; i++ {
sectionHex := hexMd5[i*8:8+i*8]
bits, _ := strconv.ParseUint(sectionHex, 16, 32)
bits = bits & 0x3FFFFFFF
keyword := ""
for j := 0; j < len; j++ {
idx := bits & 0x3D
keyword = keyword + string(charset[idx])
bits = bits >> 5
}
if cb(url, keyword) {
return keyword
}
}
return ""
}
// 起初生成6位的短码,当四组6位短码都重复时,再生成8位的短码,因此总共会有8个短码供你选择。
func GenerateShortUrl(url string, cb func(url, keyword string) bool) string {
if url == "" || cb == nil {
return ""
}
hexMd5 := fmt.Sprintf("%x", md5.Sum([]byte(url)))
sections := len(hexMd5)/8
keyword := generateCharset(url, hexMd5, 6,sections, cb)
if keyword == "" {
return generateCharset(url, hexMd5, 8,sections, cb)
}
return keyword
}