短链(短地址、Short URL)
新浪微博短链接口使用、网上对于短链实现的方法的分析
短链接产生和流行得益于社交网络的发展。短链接可以更方便的在网络中传播,避免超出字符限制,使得分享地址更加容易并且能够统计此地址的访问信息。
下面是新浪微博API对短链接口的描述:http://open.weibo.com/wiki/API%E6%96%87%E6%A1%A3_V2#.E7.9F.AD.E9.93.BE
我们最关心的是将一个长链接转化成短链接,其他内容暂不关心。
打开长了,链转短链的内容。
上面有URL,调用方式是GET方法,请求参数有三个:source、access_token、url_long,结合三个参数的说明,采用OAuth授权的方式,source不需要填写,否则source是应用的AppKey;采用OAuth授权的方式,access_token必填,内容通过OAuth授权后获得,否则不需要填写。url_long是需要转化的长链,必填。结合上面的内容,请求这个接口的典型格式有以下两种,分别是OAuth授权方式和非OAuth授权方式:
https://api.weibo.com/2/short_url/shorten.json?source=AppKey&url_long=NeedConvertURL
https://api.weibo.com/2/short_url/shorten.json?access_token=OAuthContent&url_long=NeedConvertURL
OAuth授权方式的研究自己看文档吧http://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E
非授权方式只需要一个AppKey和被转换的长链就可以了。Google以下可以获取一大堆的AppKey,如果你没有自己的应用也不想创建的话,而且这些AppKey有一个好处是用户量非常大,对接口的访问频次限制较小,相当于没有限制。
iphone新浪微博客户端 App Key:5786724301
iPad新浪客户端App Key:2849184197
Google.Nexus浪客户端App Key:1206405345
周博通微博管家App Key:202088835
Weico App Key:211160679
有了AppKey,下面测试以下获取短链接吧:
https://api.weibo.com/2/short_url/shorten.json?source=5786724301&url_long=http://www.baidu.com
结果:
{“urls”:[{“result”:true,”url_short”:”http://t.cn/h5mwx”,”url_long”:”http://www.baidu.com”,”type”:25}]}
{“urls”:[
{“result”:true,”url_short”:”http://t.cn/h5mwx”,”url_long”:”http://www.baidu.com”,”type”:25},
{“result”:true,”url_short”:”http://t.cn/h5myh”,”url_long”:”http://www.sina.com.cn”,”type”:25}
]}
返回结果中的url_short就是我们想要的内容。
下面介绍短链接服务是如何实现的。
所以短链接的原理很简单:通过一个“方法”得到长链对应的一个字符串,与拥有的短域名拼接成一个地址,访问这个地址的时候解析出原来的长链,然后跳转。
提供短链接首先必须要有一个足够短的域名。显而易见,将http://www.sina.com.cn转化成http://myshorturl.com/xysmfd的意义并不大。可以看到新浪短链接所使用的域名是t.cn,这个真的是足够短了(网易提供的短链接服务的域名是126.am),所以如果你想为别人提供短链接服务,你首先要申请到一个足够短的域名。
接着就是通过一些“方法”将传入的长链转换成一个类似于h5mwx这样的结果,然后当访问http://t.cn/h5mwx的时候找到h5mwx对应的长链,之后跳转。
目前网上查到的短链接多数都是按下面这篇文章的方式实现的。http://www.cnblogs.com/zhanghaoh/archive/2012/12/24/2831264.html
1 package com.bjdata.test; 2 3 import java.security.MessageDigest; 4 import java.util.Random; 5 6 7 public class ShortUrlTest { 8 public static void main(String[] args) { 9 String sLongUrl = "http://www.51bi.com/bbs/_t_278433840/"; // 原始链接 10 System.out.println("长链接:"+sLongUrl); 11 String[] aResult = shortUrl(sLongUrl);//将产生4组6位字符串 12 // 打印出结果 13 for (int i = 0; i < aResult.length; i++) { 14 System.out.println("[" + i + "]:" + aResult[i]); 15 } 16 Random random=new Random(); 17 int j=random.nextInt(4);//产成4以内随机数 18 System.out.println("短链接:"+aResult[j]);//随机取一个作为短链 19 } 20 21 public static String[] shortUrl(String url) { 22 // 可以自定义生成 MD5 加密字符传前的混合 KEY 23 String key = "test"; 24 // 要使用生成 URL 的字符 25 String[] chars = new String[] { "a", "b", "c", "d", "e", "f", "g", "h", 26 "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", 27 "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", 28 "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", 29 "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", 30 "U", "V", "W", "X", "Y", "Z" 31 32 }; 33 // 对传入网址进行 MD5 加密 34 String hex = md5ByHex(key + url); 35 36 String[] resUrl = new String[4]; 37 for (int i = 0; i < 4; i++) { 38 39 // 把加密字符按照 8 位一组 16 进制与 0x3FFFFFFF 进行位与运算 40 String sTempSubString = hex.substring(i * 8, i * 8 + 8); 41 42 // 这里需要使用 long 型来转换,因为 Inteper .parseInt() 只能处理 31 位 , 首位为符号位 , 如果不用long ,则会越界 43 long lHexLong = 0x3FFFFFFF & Long.parseLong(sTempSubString, 16); 44 String outChars = ""; 45 for (int j = 0; j < 6; j++) { 46 // 把得到的值与 0x0000003D 进行位与运算,取得字符数组 chars 索引 47 long index = 0x0000003D & lHexLong; 48 // 把取得的字符相加 49 outChars += chars[(int) index]; 50 // 每次循环按位右移 5 位 51 lHexLong = lHexLong >> 5; 52 } 53 // 把字符串存入对应索引的输出数组 54 resUrl[i] = outChars; 55 } 56 return resUrl; 57 } 58 /** 59 * MD5加密(32位大写) 60 * @param src 61 * @return 62 */ 63 public static String md5ByHex(String src) { 64 try { 65 MessageDigest md = MessageDigest.getInstance("MD5"); 66 byte[] b = src.getBytes(); 67 md.reset(); 68 md.update(b); 69 byte[] hash = md.digest(); 70 String hs = ""; 71 String stmp = ""; 72 for (int i = 0; i < hash.length; i++) { 73 stmp = Integer.toHexString(hash[i] & 0xFF); 74 if (stmp.length() == 1) 75 hs = hs + "0" + stmp; 76 else { 77 hs = hs + stmp; 78 } 79 } 80 return hs.toUpperCase(); 81 } catch (Exception e) { 82 return ""; 83 } 84 } 85 86 }
这个算法有值得改进的地方:
- 它最后为了62进制,使用的是0x0000003D,转成二进制最后几位是111101,表示62,用它进行与运算的话会屏蔽掉二进制表示时第二位为1的数,因此屏蔽掉了接近一半的内容(因为不是1就是0,所以屏蔽的1就相当于砍掉了一半,0-61中有30个数被看去),其实它是32进制了。
- MD5加密结果分为8位一组,最后产生4个短地址,这样做重复的概率被大大提高了。32位中8位一组分4组后随机选一组的概率出现相同的结果的概率远大于32位MD5结果重复的概率。
对于上面的两个问题,我觉得是否可以使用64进制,如果计算出的结果是62、或者63,则随机取一个数组中的值解决0x0000003D引起的问题;第二个一次提供四个短地址是否是为了出现重复的情况下重新选择呢?
希望看到这篇博客的博友们能给点自己的思路,如果能提供Google的短地址或新浪的短地址实现的方法就更好了(他们提供的短地址好像重4位-7位的样子,不会是一个sequence转成62进制吧?!)。
(在一篇博文中看到博主说他在网上看到这个算法是C#版的,将它翻译成Java版,我还是挺欣赏的,毕竟人家思考了,写了自己的东西。但就这么一段内容,好多“原创”博文,都不著名出处,也没有自己的思考……诶……)