这篇文章上次修改于 943 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

开坑

仔细算算这个项目也差不多干了半个月了,从一开始的想法到付诸实践真的很难,但在了解到伪静态之后一切都明了了,只是具体细节还没想好.

项目地址:传送门

  • 首先第一步是把将十进制id存入数据库,再以62进制进行输出,一开始使用的是数组+while循环实现,但这样过于浪费资源,使用二维数组的话就很方便了,但是62个字母数字,打起来太累了,后面想到了用Python来输出,直接把一维数组复制过去用for循环跑两遍就好了
$encryption = [
        0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9',
        10 => 'a', 11 => 'b', 12 => 'c', 13 => 'd', 14 => 'e', 15 => 'f', 16 => 'g', 17 => 'h', 18 => 'i', 19 => 'j', 20 => 'k', 21 => 'l', 22 => 'm', 23 => 'n', 24 => 'o', 25 => 'p', 26 => 'q', 27 => 'r', 28 => 's', 29 => 't', 30 => 'u', 31 => 'v', 32 => 'w', 33 => 'x', 34 => 'y', 35 => 'z',
        36 => 'A', 37 => 'B', 38 => 'C', 39 => 'D', 40 => 'E', 41 => 'F', 42 => 'G', 43 => 'H', 44 => 'I', 45 => 'J', 46 => 'K', 47 => 'L', 48 => 'M', 49 => 'N', 50 => 'O', 51 => 'P', 52 => 'Q', 53 => 'R', 54 => 'S', 55 => 'T', 56 => 'U', 57 => 'V', 58 => 'W', 59 => 'X', 60 => 'Y', 61 => 'Z'
    ];
    $decryption = [
        '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
        'a' => 10, 'b' => 11, 'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17, 'i' => 18, 'j' => 19, 'k' => 20, 'l' => 21, 'm' => 22, 'n' => 23, 'o' => 24, 'p' => 25, 'q' => 26, 'r' => 27, 's' => 28, 't' => 29, 'u' => 30, 'v' => 31, 'w' => 32, 'x' => 33, 'y' => 34, 'z' => 35,
        'A' => 36, 'B' => 37, 'C' => 38, 'D' => 39, 'E' => 40, 'F' => 41, 'G' => 42, 'H' => 43, 'I' => 44, 'J' => 45, 'K' => 46, 'L' => 47, 'M' => 48, 'N' => 49, 'O' => 50, 'P' => 51, 'Q' => 52, 'R' => 53, 'S' => 54, 'T' => 55, 'U' => 56, 'V' => 57, 'W' => 58, 'X' => 59, 'Y' => 60, 'Z' => 61
    ];

实现

  • 一开始的想法是自增短链uid,但是这样转换生成的surl过于简单,而且与各平台的相差甚大,于是后来又想到了不再自增1,虽然初步解决了0太多的问题,但是还是和其他短链服务的有本质区别.现在的方案是使用异或来进行最简单的加密,这样保证了数字不会太小,转换成短码之后也看起来更像随机的.

关于相邻几个短码的规律

  • 这个我想过了,但我不太会.我做不到让比如三个相邻数的短码看起来毫无关联(就像MD5码那样),而且因为我在数据库里存的是十进制,所以不是简简单单的加密就可以了,还得去解密.虽然使用一些加密函数比如sha可以直接做到,但是最少也是16位的校验码,这看上去和项目名是背道而驰.虽然取hash码的前6位确实是可以的,但是理论上还是存在很大概率碰撞的,这是不符合逻辑的,即一个长链对应唯一一个短链,一个短链指向的长链也是不会被修改的(update)
  • 虽然也有可以进行加密再解密的方法,但那些过于复杂了,我觉得这个项目也用不到,有损加密虽然快,但是取六位存在较大概率发生碰撞
  • 但是,也不是完全不能做到.虽然不能做到看上去毫无关联,但是可以做到看起来不太像.

上面代码块中,0~Z是严格一一对应十进制的0~61的,那我只需要打乱他们的对应顺序,就可以实现看上去无序了(确实无序).然后再进一步,让相邻数不存在,62^6是一个很大的数,因此即使一次加上几千几万也和1没什么区别,至少我这个是完全没影响的,可以让插入的数据短码为上一条的加上random(114,514)这样,然后再对六位短码进行打乱,比如原来的是[1,2,3,4,5,6],我输出的时候改成[3,2,5,4,6,1]

细节

校验

因为发送数据的形式是Ajax,所以即使给input设置了type="url" required="required"属性也一点用都没有,但输入的如果不是链接那就没有意义了,虽然一段文字也可以生成二维码,但是短链接的原理无非就是重定向,获取到的如果不是链接那就无法成功重定向,这个问题使用万能的正则就好了.

生成二维码

以PHP和Python的作用来说,使用Python来生成QRCode是更好的选择,而且还有现成的第三方库可以调用,之前使用过一次PHP绘图,当时报错信息差不多是拓展库里没有这个函数(image)所以我都没考虑PHP绘图.

使用MyQr函数运行之后确实生成了二维码,但是我的需求是使用PHP来传参执行py脚本,自己摸索了几小时之后算是搞懂了,使用exec passthru system这三个都行,只是第二个不会返回脚本的输出结果.

访问统计

这个轻车熟路了,直接复制我图片API的统计代码改一改就好了

其他

  • 想了想,返回二维码的脚本应该直接输出并且只有输出,而不是生成图片再重定向,因为这样对于信息来说更安全,不会暴露关键路径.但是这样貌似得加一个sleep来等待二维码的生成,以免出现找不到图片,我想到的处理办法是把生成图片的部分给挪回插入脚本里,但是这样会延长插入返回JSON的速度,导致短链图片都没加载好,还是挺麻烦的.
  • 关于移植到服务器上.主要还是路径,编码和权限的问题.这些捣鼓几小时之后也就搞好了,搞这些的时候还是得靠自己,百度查不到的,最后是靠我直觉找到问题的.