• 我想说说长短链接



    前言

    最近开发了一套推广功能,将其中涉及的长短链接问题在这里分享一下。推广方式主要是以短信方式慰问客户并推送宣传链接(非广告),但链接真的是太长了,先不说短信按字数收费问题,就是看到就想立刻删除。这就让小编开启了新的征程,研究如何让链接变短,精简干练。。。

    关于长短链接

    • 长链接:顾名思义,就是网页的完整URL地址,点击即可跳转至网页,进行内容浏览。
    • 短链接:就是将长链接进行处理后转换成长度较小的URL地址,如 https://sourl.cn/upNbxj 则是对应 https://blog.csdn.net/qq_39486758/article/details/126602389
    • 短链接相较于长链接,会更简短,便于一些第三方平台的字符长度限制等问题处理,当然对于小编来说,可以省下不少短信费用~~~

    链接解析原理

    • 当我们在网站输入短链接后,DNS解析链接的ip地址(即短链接服务器),然后DNS转发请求(HTTP GET方式)至服务器,通过短链接码获取对应的完整URL地址,最后服务器通过请求(HTTP 301)转到完整URL地址,至此完成解析。时序图奉上:短链接解析
    • 注:短链接跳转长链接可以采用301(永久重定向),也可以采用302(临时重定向),区别就是对资源的管理,301会将旧资源永久移除,替换为重定向的新资源;而302还是会保留旧资源,只是重定向到新资源,并不会发生替换,也不会保存新资源。

    长短链接转换案例

    免费的在线工具:现在网上有很多开源的项目,可以供小白免费使用(但有些白嫖需要注册,毕竟都需要生活),而且有些开源工具建设十分完善,基本满足了各种长短链接需求。推荐几个小编觉得好用的免费工具:

    • 站长之家:用的比较多的在线工具平台(需要注册信息)
    • 短网址:可以设置有效期,访问密码,相对步骤简单。

    自研短链接服务:由于开源项目存在不确定性,不得不自己搭建一套短链接服务,满足使用需求。一是便于维护,二是可以灵活扩展。接下来结合代码进行分析:

    • 参照上面原理,即可分析出需要搭建一套负责生成、解析、转发的服务,由于小编是在本地演示,直接通过ip地址调用处理,可以更加直接了解其过程。
    • 首先是生成短链接码的算法工具类,算法不是固定的,可以根据自己习惯或工作要求使用其它的算法生成,最主要是保证短链接码的唯一性。
    /**
     * @description: 进制转换工具
     * B(Binary)表示二进制
     * O(Octal)表示八进制
     * D(Decimal)表示十进制
     * H(Hexadecimal)表示十六进制
     * 62进制
     */
    public class BaseConvertUtil {
    
        // 62进制转换率
        private static int SCALE_62 = 62;
        // 62进制,索引位置代表转换字符的数值 0-61,比如 A代表10,z代表61
        private static String CHARS_62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    
        /**
         * 十进制数字转换为62进制字符串
         * 0123456789  0-9
         * ABCDEFGHIJKLMNOPQRSTUVWXYZ 10-35
         * abcdefghijklmnopqrstuvwxyz 36-61
         *
         * @param value 十进制数字
         * @return 62进制字符串
         */
        public static String encode10to62(long value) {
            if (value < 0) {
                throw new IllegalArgumentException("参数非法(必须为非负数): " + value);
            }
            StringBuilder stringBuilder = new StringBuilder();
            while (value > SCALE_62 - 1) {
                stringBuilder.append(CHARS_62.charAt((int) (value % SCALE_62)));
                value = value / SCALE_62;
            }
            // 获取最高位
            stringBuilder.append(CHARS_62.charAt((int) (value % SCALE_62)));
            return stringBuilder.reverse().toString();
        }
    
    
        /**
         * 将10进制数字转换为长度为length的62进制字符串
         * 原始62进制字符串长度小于length,左侧用‘0’填充补齐
         *
         * @param value  十进制数字
         * @param length 长度
         * @return 长度为length或大于length的62进制字符串
         */
        public static String encode10to62(long value, int length) {
            if (length < 1) {
                throw new IllegalArgumentException("参数非法(长度必须大于0): " + value);
            }
            String str62Base = encode10to62(value);
            if (str62Base.length() < length) {
                long num = (long) Math.pow(10, length);
                str62Base = num + str62Base;
                str62Base = str62Base.substring(str62Base.length() - length);
            }
            return str62Base;
        }
    
        /**
         * 62进制编码转换为10进制编码
         *
         * @param str62Base 62进制编码
         * @return 十进制编码
         */
        public static long encode62to10(String str62Base) {
            if (str62Base == null || !str62Base.matches("[a-zA-Z\\d]+")) {
                throw new IllegalArgumentException("参数非法(非62进制): " + str62Base);
            }
            int length = str62Base.length();
            long value = 0;
            for (int index = 0; index < length; index++) {
                value = value * SCALE_62 + base62To10(str62Base.charAt(index));
            }
            return value;
        }
    
        /**
         * 62进制字符转换成对应十进制表示
         * 根据ASCII字符代码表
         * 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
         *
         * @param base62 62进制
         * @return 十进制
         */
        private static int base62To10(char base62) {
            int value = base62;
            // ‘0-9’  0-9
            // ‘0’ ASCII字符代码表 十进制48
            // ‘9’ ASCII字符代码表 十进制57
            if (value <= 57) value = value - 48;
                // ‘A-Z’  10-35
                // ‘A’ ASCII字符代码表 十进制65
                // ‘Z’ ASCII字符代码表 十进制90
            else if (value <= 90) value = value - 65 + 10;
                // ‘a-z’  36-61
                // ‘a’ ASCII字符代码表 十进制97
                // ‘Z’ ASCII字符代码表 十进制122
            else value = value - 97 + 36;
            return value;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 然后就是维护短链接及关系映射,此处小编采用的是集合变量,建议采用Mysql等数据库将关系数据持久化,避免数据丢失,导致访问失败。
        /*
         * 短链接服务器地址  根据自己实际场景替换
         * */
        private String smartUrlDomainName = "http://192.168.10.127:8822";
    
        /*
        * 短链接与长链接映射关系集合
        * */
        private Map<Long, String> urlMap = new HashMap<>();
    
        /**
         * 长链接编码成短链接
         *
         * @param originUrl 原始链接(长链接)
         * @return 短链接
         */
        public String encode(String originUrl) {
            // 依据时间戳作为发号器,转化为62进制(只包含数字、大小写字母)
            long id = System.currentTimeMillis();
            String smartCode = BaseConvertUtil.encode10to62(id, 5);
            urlMap.put(id, originUrl);
            return smartUrlDomainName + "/r/" + smartCode;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 提供转发处理接口,本质就是访问短链接服务的接口,完成解析到重定向的处理,至此,短链接服务器完成使命(同时在处理过程中可以增加访问记录等埋点操作)。
        /**
         * 解码重定向
         *
         * @param url 原始链接的编码
         * @return 重定向
         */
        @GetMapping("/r/{url}")
        public ModelAndView redirect(@PathVariable String url) {
            long id = BaseConvertUtil.encode62to10(smartUrl);
            String originUrl = urlMap.get(id);
            RedirectView redirectView=new RedirectView(originUrl);
            // 301永久重定向,避免网络劫持
            redirectView.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
            return new ModelAndView(redirectView);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 模拟操作过程:本地启动短链接服务,再启动一个业务服务作为长链接服务,将长链接生成短链接,然后访问短链接并成功跳转至长链接地址。演示结果
      长链接换短链接
      访问短链接
      重定向长连接

    总结

    • 以上就是小编分享的全部内容,当然不止这一种实现方式,有想法的小伙伴可以私信探讨。
    • 本文中涉及的网络技术点并没有进行详细介绍,这块有时间小编会单独整理,以便大家一起学习。
    • 如果需要源码,点此即可获取
  • 相关阅读:
    JVM篇---第九篇
    Midjourney 实现角色一致性的新方法
    JavaScript endsWith() 方法
    mac 安装allure
    React props属性使用及子传父组件使用
    API设计笔记:抽象基类、工厂方法、扩展工厂
    程序员都无法理解的代码
    HotReload for unity支持的代码修改
    04 Pytorch tensor
    torch.as_tensor()、torch.Tensor() 、 torch.tensor() 、transforms.ToTensor()的区别
  • 原文地址:https://blog.csdn.net/qq_39486758/article/details/126692489