前言

前段时间做支付的时候,要生成可供微信和支付宝扫描的支付二维码,二维码里存储的也就是一个链接,但是可能是因为链接比较长的缘故,总是被微信拦截。

然后就换成了微信官方的长链接转短链接,好不容易弄好了,发现支付宝扫的话会拦截,两家果真是水火不容。。。

有问题就解决问题呗,那我换成第三方的好了,但是网上一般的第三方短链接服务平台都不会被微信接纳,那就只能找比较知名的,比如新浪、百度、谷歌,我首先试了新浪的,完美,终于让微信和支付宝都满意了。

然后就在前几周,测试人员忽然找我说二维码出不来了。。我真是心力交瘁,去新浪官方一看你说气不气,在11号的时候这个接口下线了。。。

20190923111827371.png

靠别人永远靠不住。。。那就只能靠自己了!

没有接触过这种业务的小伙伴可能会问什么是短链接?长链接好好地,干嘛要把它转成短链接?咋就能转成短链接呢?

下面我们简单介绍一下短链接的相关知识。

什么是短链接

比如一个百度图片的搜索网址是这样的:http://image.baidu.com/search/index?tn=baiduimage&ct=201326592&lm=-1&cl=2&ie=gb18030&word=%CD%BC%C6%AC&fr=ala&ala=1&alatpl=others&pos=0

以及一些第三方的:

看到没有,一个那么长的网址,确被缩短到这么短,而且每个平台生成的都不一样,缺都可以访问到我们原本那个长长的网址。以上生成的网址有些可能是永久的,有些可能一段时间就过期了。

为什么要用短链接(引用)

1、新浪微博

我们在新浪微博上发布网址的时候,微博会自动判别网址,并将其转换,例如:https://t.cn/RuPKzRW。为什么要这样做的?

这是因为微博限制字数为140字一条,那么如果我们需要发一些链接上去,但是这个链接非常的长,以至于将近要占用我们内容的一半篇幅,这肯定是不能被允许的或者说用户体验很差的,所以短网址应运而生了,短网址这种服务可以说是在微博出现之后才流行开来的!往下看:

(1)首先,我先发一条微博带有一个URL地址: 

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9VdFdkRGd5bkxkYUxLT2Q0NjNZcWpJZWQwaWFKNmljaWJUSkNVVW1pYUt6UExLbm8yZGV1V0tNVXFkQ1h5d2F4WTU0c21DeUFET1dSN1ViMVBMN3JTWlQybUEvNjQw.jpg

(2)然后,看他转换之后显示的效果是什么样子的哪?

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9VdFdkRGd5bkxkYUxLT2Q0NjNZcWpJZWQwaWFKNmljaWJUSk5SbXVLeTI5N2t5aHdHUExvZ0t1SzgzcGljQ1FIVTAyRkJhaWFwOEE3dDFsbkh3SDFxTE02ZG5BLzY0MA.jpg

(3)查看对应页面元素的HTML源码如下:

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9VdFdkRGd5bkxkYUxLT2Q0NjNZcWpJZWQwaWFKNmljaWJUSkdIQWZpYTF3SXhTSHpxaE11U0RLUGlhRUo3YlVKQWtRYUF3dkZpYnRlVXZEY2JFVWliRWplMmtMZGcvNjQw.jpg

(4)可以看出:https://blog.csdn.net/xlgen157387/article/details/79863301 被转换为:http://t.cn/RuPKzRW,此时你访问http://t.cn/RuPKzRW是可以定位到https://blog.csdn.net/xlgen157387/article/details/79863301,也就是实现了转换。

2、短网址二维码

网址在转换成短网址时,也可以生成相应的短网址二维码,短网址二维码的应用,二维码核心解决的是跨平台、跨现实的数据传输问题;而且二维码跟应用场景结合之后,所能解决的问题会越来越多。

(1)短网址二维码相比短链接更方便,能少输入,尽量少输入,哪怕只是少点一下键盘,都是有意义的。

(2)二维码只是扫描一个简单的链接,打开的却是一个世界。想象一下,用手机购买售货机里商品,二维码扫描是略快于从用手机找到该售货机并找到该商品的,而且这种操作相对于搜索/查找而言不是更优雅吗?

(3)所有商超里面的商品,都是使用条码来确定商品的唯一性的,去买单的时候都是扫描条码。试想,如果里面加入了更多产品的生产日期、厂家、流转途径、原材料等等信息,是不是厉害了呢?特别是针对食品信息的可追溯上,二维码应用场景更广泛。
除了上述场景中,我们将长地址转换为短地址的使用场景的优点(压缩URL长度)之外,短地址还具有很多实际场景中的优点,例如:

(1)节省网址长度,便于社交化传播,一个是让URL更短小,传播更方便,尤其是URL中有中文和特殊字符,短网址解决很长的URL难以记忆不利于传播的问题;

(2)短网址在我们项目里可以很好的对开放以及对URL进行管理。有一部分网址可以会涵盖性、暴力、广告等信息,这样我们可以通过用户的举报,完全管理这个连接将不出现在我们的应用中,对同样的URL通过加密算法之后,得到的地址是一样的;

(3)方便后台跟踪点击量、地域分布等用户统计。我们可以对一系列的网址进行流量,点击等统计,挖掘出大多数用户的关注点,这样有利于我们对项目的后续工作更好的作出决策;

(4)规避关键词、域名屏蔽手段、隐藏真实地址,适合做付费推广链接;

(5)当你看到一个淘宝的宝贝连接后面是200个“e7x8bv7c8bisdj”这样的字符的时候,你还会觉得舒服吗。更何况微博字数只有140字,微博或短信里,字数不够,你用条短网址就能帮你腾出很多空间来;

如何生成短链接

网上比较流行的算法有两种 自增序列算法、 摘要算法

这里我也没去专门很细的研究,着急拿来用,后续有时间再去深入吧,有兴趣的小伙伴可以看一下以下博客:

面试必备:如何将一个长URL转换为一个短URL?
短网址(short URL)系统的原理及其实现

实现

好了,说了这么多,终于到实现的部分了,其实短网址生成服务基本的实现流程都是一样的,不一样的知识短网址生成的算法不同而已,下面是一个短网址系统基本的实现,我这里是存在redis中比较方便快捷,大家也可以选择存入mysql等。

111.png

代码

这里的案例场景模仿我遇到的支付问题
案例是基于springboot项目,其他框架可能要做适当修改

OrderPayController:项目内生成二维码的接口,这里简化,只是生成链接,未转变为二维码,想转为二维码的可以看:【二维码】——生成二维码并转为base64

OrderController:订单接口

 /**
 \* @author: WangZhiJun
 \* @create: 2019-09-21 12:31
 **/
@RestController
@RequestMapping("/api/v1/order")
public class OrderController {
    private static final String SHORT_URL = "shortUrl:";
 
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
 
    /**
     * 系统内生成支付二维码接口
     */
    @GetMapping(value = "/payOrder")
    public String pay(@RequestParam String orderId) {
        String orderName = "XXSuperMarketPay";
        BigDecimal orderMoney = new BigDecimal(100);
        System.out.println("订单"+orderId+orderName+"需支付:"+orderMoney+"元");
 
        String wxPayUrl = "http://localhost:8080/wx/pay/pay?orderId="+orderId+"&orderName="+orderName+"&orderMoney="+orderMoney;
        String shortUrl = ShortUrlUtils.shortUrl(wxPayUrl);
        System.out.println("生成的微信支付链接为:"+wxPayUrl);
        System.out.println("生成的短链接为:"+ shortUrl);
        //存入redis或者数据库,键值为六位数的短链接
        redisTemplate.opsForValue().set(SHORT_URL + shortUrl, shortUrl, 1, TimeUnit.HOURS);
        //这里我的业务场景中是生成二维码,这里简化为链接
        return "http://localhost:8080/url/" + shortUrl;
    }
}

WxPayController:模仿微信支付官方接口

/** 假设这是微信支付的接口
 * @author: WangZhiJun
 * @create: 2019-09-21 12:31
 **/
@RestController
@RequestMapping("/wx/pay")
public class WxPayController {
    /**
     * 模仿微信支付API
     */
    @GetMapping(value = "/pay")
    public void paySuccess(@RequestParam String orderId,
                         @RequestParam String orderMoney,
                         @RequestParam String orderName) {
        System.out.println("订单"+orderId+orderName+"支付:"+orderMoney+"元");
        System.out.println("支付成功");
    }
}

UrlController:短码转发,将短码对应的长链接取出并重定向到长链接

/**
 * @description: 短网址转发
 * @author: WangZhiJun
 * @create: 2019-09-21 12:20
 **/
@RestController
@RequestMapping("/url")
public class UrlController {
    private static final String SHORT_URL = "shortUrl:";
 
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
 
    @GetMapping(value = "/{shortUrl}")
    public void getInvoiceUrl(@PathVariable(required = false) String shortUrl,
                              HttpServletResponse response) {
        String url = (String) redisTemplate.opsForValue().get(SHORT_URL + shortUrl);
        try {
            //将短链接映射的长链接取出并重定向到长链接
            response.sendRedirect(url);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

长链接转短码的算法主要是使用MD5 算法对原始链接进行加密(这里使用的MD5 加密后的字符串长度为32 位),然后对加密后的字符串进行处理以得到短链接的地址。

长链接转短码工具类

import java.security.MessageDigest;
 
/** 短链接生成工具类
 * @author WangZhiJun
 */
public class ShortUrlUtils {
 
    /**
     * 十六进制下数字到字符的映射数组
     */
    private final static String[] HEXDIGITS = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
    /**把inputString加密*/
    public static String md5(String inputStr){
        return encodeByMD5(inputStr);
    }
 
    /**对字符串进行MD5编码*/
    private static String encodeByMD5(String originString){
        if (originString!=null) {
            try {
                //创建具有指定算法名称的信息摘要
                MessageDigest md5 = MessageDigest.getInstance("MD5");
                //使用指定的字节数组对摘要进行最后更新,然后完成摘要计算
                byte[] results = md5.digest(originString.getBytes());
                //将得到的字节数组变成字符串返回
                String result = byteArrayToHexString(results);
                return result;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    /**
     * 轮换字节数组为十六进制字符串
     * @param b 字节数组
     * @return 十六进制字符串
     */
    private static String byteArrayToHexString(byte[] b){
        StringBuffer resultSb = new StringBuffer();
        for(int i=0;i<b.length;i++){
            resultSb.append(byteToHexString(b[i]));
        }
        return resultSb.toString();
    }
 
    /**
     * @param b 字节
     * @return 将一个字节转化成十六进制形式的字符串
     */
    private static String byteToHexString(byte b){
        int n = b;
        if(n<0) {
            n = 256 + n;
        }
        int d1 = n/16;
        int d2 = n%16;
        return HEXDIGITS[d1] + HEXDIGITS[d2];
    }
 
    public static String shortUrl(String url) {
        return shortUrls(url)[0];
    }
 
    public static String[] shortUrls(String url) {
        // 可以自定义生成 MD5 加密字符传前的混合 KEY
        String key = "md5" ;
        // 要使用生成 URL 的字符
        String[] chars = new String[] { "a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" ,
                "i" , "j" , "k" , "l" , "m" , "n" , "o" , "p" , "q" , "r" , "s" , "t" ,
                "u" , "v" , "w" , "x" , "y" , "z" , "0" , "1" , "2" , "3" , "4" , "5" ,
                "6" , "7" , "8" , "9" , "A" , "B" , "C" , "D" , "E" , "F" , "G" , "H" ,
                "I" , "J" , "K" , "L" , "M" , "N" , "O" , "P" , "Q" , "R" , "S" , "T" ,
                "U" , "V" , "W" , "X" , "Y" , "Z"
        };
        // 对传入网址进行 MD5 加密
        String hex = ShortUrlUtils.md5(key + url);
        String[] resUrl = new String[4];
        for ( int i = 0; i < 4; i++) {
            // 把加密字符按照 8 位一组 16 进制与 0x3FFFFFFF 进行位与运算
            String sTempSubString = hex.substring(i * 8, i * 8 + 8);
            // 这里需要使用 long 型来转换,因为 Inteper .parseInt() 只能处理 31 位 , 首位为符号位 , 如果不用 long ,则会越界
            long lHexLong = 0x3FFFFFFF & Long.parseLong (sTempSubString, 16);
            StringBuilder outChars = new StringBuilder();
            for ( int j = 0; j < 6; j++) {
                // 把得到的值与 0x0000003D 进行位与运算,取得字符数组 chars 索引
                long index = 0x0000003D & lHexLong;
                // 把取得的字符相加
                outChars.append(chars[(int) index]);
                // 每次循环按位右移 5 位
                lHexLong = lHexLong >> 5;
            }
            // 把字符串存入对应索引的输出数组
            resUrl[i] = outChars.toString();
        }
        return resUrl;
    }
}

演示

20190921131108317.png

20190921131834218.png

然后访问生成的短码网址:http://localhost:8080/url/vqQjEj,会自动重定向到原本的支付链接

20190921131409786.png

可以在博主主页项目部分进行测试短网址服务

大功告成!


版权声明:文章转载请注明来源,如有侵权请联系博主删除!
最后修改:2020 年 01 月 04 日 05 : 42 PM
如果觉得我的文章对你有用,请随意赞赏
评论打卡也可以哦,您的鼓励是我最大的动力!