【短鏈接】——自己實現一個短網址服務

目錄

前言

什麼是短鏈接

爲什麼要用短鏈接(引用)

如何生成短鏈接

實現

代碼

演示


前言

前段時間做支付的時候,要生成可供微信和支付寶掃描的支付二維碼,二維碼裏存儲的也就是一個鏈接,但是可能是因爲鏈接比較長的緣故,總是被微信攔截。

然後就換成了微信官方的長鏈接轉短鏈接,好不容易弄好了,發現支付寶掃的話會攔截,兩家果真是水火不容。。。

有問題就解決問題唄,那我換成第三方的好了,但是網上一般的第三方短鏈接服務平臺都不會被微信接納,那就只能找比較知名的,比如新浪、百度、谷歌,我首先試了新浪的,完美,終於讓微信和支付寶都滿意了。

然後就在前幾周,測試人員忽然找我說二維碼出不來了。。我真是心力交瘁,去新浪官方一看你說氣不氣,在11號的時候這個接口下線了。。。

靠別人永遠靠不住。。。那就只能靠自己了!

沒有接觸過這種業務的小夥伴可能會問什麼是短鏈接?長鏈接好好地,幹嘛要把它轉成短鏈接?咋就能轉成短鏈接呢?

下面我們簡單介紹一下短鏈接的相關知識。

什麼是短鏈接

顧名思義,就是將長網址縮短到一個很短的網址,用戶訪問這個短網址可以就可以訪問到原本的長網址。這樣可以達到易於記憶、轉換的目的。

比如一個百度圖片的搜索網址是這樣的: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地址: 

(2)然後,看他轉換之後顯示的效果是什麼樣子的哪? 

(3)查看對應頁面元素的HTML源碼如下: 

(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等。

代碼

這裏的案例場景模仿我遇到的支付問題

案例是基於springboot項目,其他框架可能要做適當修改

OrderPayController:項目內生成二維碼的接口,這裏簡化,只是生成鏈接,未轉變爲二維碼,想轉爲二維碼的可以看:【二維碼】——生成二維碼並轉爲base64

/**
 * @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;
    }
}

演示

 然後訪問生成的短碼網址:http://localhost:8080/url/vqQjEj,會自動重定向到原本的支付鏈接

實現效果可以去我的個人主頁進行測試:短網址服務

大功告成!

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章