目錄
1.背景介紹
2.短鏈跳轉的意義? ??
3.SpringBoot中的代碼實現
1.建議短鏈-長鏈的數據庫表:t_url_map:
2.映射實體
3.Dao層實現
4.Service層實現
5.Controller層實現?
3.結果測試
4.問題
1.背景介紹
????????短鏈跳轉是一種通過將長鏈接轉換為短鏈接的方式,以便在互聯網上進行鏈接共享和傳播的技術。通常情況下,長鏈接可能由于包含大量參數或者較長的路徑而顯得復雜且不易記憶,而短鏈則是將原始長鏈接通過特定算法轉換為較短的鏈接,使得它更容易分享、傳播和展示。
????????短鏈跳轉服務通常由第三方提供,用戶可以將需要縮短的長鏈接提交到該服務,服務會返回一個短鏈接,當用戶訪問這個短鏈接時,會被重定向到原始的長鏈接地址。這種服務通常還提供了統計功能,可以跟蹤短鏈接被點擊的次數、訪問來源等信息,幫助用戶了解鏈接的傳播效果。
????????短鏈跳轉服務有助于美化鏈接、節省空間、方便分享和統計鏈接訪問情況,因此被廣泛應用于社交媒體、微博客、推廣活動等各種互聯網應用場景中。
? ? ? ? 比如在b站中,一個視頻的網址原來是這樣的:
? ? ? ? 在移動端中,點擊分享按鈕,復制其鏈接:
? ? ? ?
????????它會變成如下鏈接形式:
? ? ? 【Cookie、Session、Token、JWT一次性講完-嗶哩嗶哩】 https://b23.tv/0SMtYq6
? ? ? ? 點擊該鏈接后,你會發現瀏覽器的網址URL為原來的長鏈接形式,也就是說這其中發生了重定向?,而這個過程就是這篇博客要提到的短鏈跳轉了。
2.短鏈跳轉的意義? ??
-
節省空間:長鏈接可能會很長,不方便分享或展示,通過短鏈跳轉可以將長鏈接轉換為短鏈接,節省字符空間。
-
美化鏈接:短鏈看起來更簡潔、美觀,對于需要展示給用戶或發布到社交媒體等場景更具吸引力。
-
防止鏈接失效:某些長鏈接可能會因為過期、失效或變動而無法訪問,通過短鏈跳轉可以在后臺進行管理和更新,保證鏈接的可訪問性。
-
統計和跟蹤:通過短鏈跳轉服務可以對鏈接的點擊量、來源、地域等信息進行統計和分析,幫助用戶了解鏈接的受眾和效果。
-
方便分享:短鏈更容易復制、粘貼和分享,適用于短信、微博、郵件等分享場景,提高分享效率。
-
隱藏原始鏈接:有時候希望隱藏原始鏈接的信息,通過短鏈跳轉可以起到一定的保護作用,防止泄露敏感信息。
3.SpringBoot中的代碼實現
? ? ? ? 這里我們以快速入門為主,即主要實現長鏈到短鏈的映射邏輯。
1.建議短鏈-長鏈的數據庫表:t_url_map:
2.映射實體
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.time.Instant;@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UrlMap {private Long id;private String longUrl;private String shortUrl;private String username;private Instant expireTime;private Instant creationTime;}
????????這里要添加lombok依賴:
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
3.Dao層實現
? ? ? ? 這里簡單寫三個關鍵的接口方法:根據長鏈找短鏈(若無則生成短鏈)、根據短鏈找長鏈(若無則跳轉失敗頁面)、插入實體
import com.zhan.zhan215.Entity.UrlMap;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.time.Instant;
import java.util.List;@Mapper
public interface UrlMapMapper {UrlMap findFirstByLongUrl(@Param("longUrl") String longUrl, @Param("username") String username);void saveUrlMap(UrlMap urlMap);UrlMap findByShortUrl(String shortUrl);}
? ? ? ? 對應的xml映射:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhan.zhan215.Dao.UrlMapMapper"><select id="findFirstByLongUrl" parameterType="string" resultType="com.zhan.zhan215.Entity.UrlMap">select *from t_url_mapwhere longUrl =#{longUrl} and username = #{username}limit 1</select><!-- 在Mapper XML文件中定義保存urlMap對象的方法 --><insert id="saveUrlMap" parameterType="com.zhan.zhan215.Entity.UrlMap">INSERT INTO t_url_map (shortUrl, longUrl,username)VALUES (#{shortUrl}, #{longUrl},#{username})</insert><select id="findByShortUrl" parameterType="string" resultType="com.zhan.zhan215.Entity.UrlMap">select *from t_url_mapwhere shortUrl = #{shortUrl} limit 1</select>
4.Service層實現
import com.zhan.zhan215.Dao.UrlMapMapper;
import com.zhan.zhan215.Entity.UrlMap;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;@Service
public class UrlMapService {@Resourceprivate UrlMapMapper urlMapMapper;// 編碼public String encode(String longUrl,String username) {UrlMap urlMap = urlMapMapper.findFirstByLongUrl(longUrl,username);// 看看該長鏈接是否存在// 如果存在并且其對應用戶名等于已有用戶名,則直接給出短鏈接if (urlMap != null&&username.equals(urlMap.getUsername())) {return urlMap.getShortUrl();} else {// 如果不存在,則生成短鏈接UrlMap urlMap1 = new UrlMap();// 生成短鏈接String shortLink = generateShortLink(longUrl,username);// 保存短鏈接urlMap1.setLongUrl(longUrl);urlMap1.setShortUrl(shortLink);urlMap1.setUsername(username);urlMap1.setCreationTime(Instant.now());urlMapMapper.saveUrlMap(urlMap1);return shortLink;}}// 解碼public String decode(String shortUrl){// 根據短鏈接獲取長鏈接UrlMap byShortUrl = urlMapMapper.findByShortUrl(shortUrl);// 如果存在,返回長鏈接if(byShortUrl!=null){return byShortUrl.getLongUrl();}// 如果沒有,返回首頁(正常是返回一個失敗頁面)return "https://bilibili.com";}// 生成短鏈接public static String generateShortLink(String originalUrl,String username) {try {MessageDigest md = MessageDigest.getInstance("MD5");byte[] hashBytes = md.digest(originalUrl.getBytes());// 對原始URL進行MD5哈希計算StringBuilder sb = new StringBuilder();for (byte b : hashBytes) {sb.append(String.format("%02x", b));// 將字節數組轉換為十六進制字符串}return sb.toString().substring(0, 8)+username;// 截取前8位,加上用戶名(這里先簡單默認用戶名是4位數)} catch (NoSuchAlgorithmException e) {e.printStackTrace();return null;}}}
5.Controller層實現?
import com.zhan.zhan215.Common.ResponseBean;
import com.zhan.zhan215.Service.UrlMapService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.view.RedirectView;import javax.annotation.Resource;@RestController
public class UrlMapController {@Resourceprivate UrlMapService urlMapService;@PostMapping("/shorten")// 長鏈接轉短連接,相當于實際項目中的點擊“分享”,形成一條短連接public ResponseBean shorten(@RequestParam String longUrl,@RequestParam String username){String encode = urlMapService.encode(longUrl,username);// 形成短鏈return ResponseBean.success(encode);}@GetMapping("redirect")//重定向public RedirectView redirectView(@RequestParam String shortUrl){String longUrl = urlMapService.decode(shortUrl);return new RedirectView(longUrl);}}
相關的ResponseBean的返回結果集代碼:
public class ResponseBean<T> {/** 200:操作成功 -1:操作失敗**/// http 狀態碼private boolean success;// 返回的數據private T data;public boolean isSuccess() {return success;}public void setSuccess(boolean success) {this.success = success;}public T getData() {return data;}public void setData(T data) {this.data = data;}public static <T> ResponseBean<T> success(T data) {ResponseBean<T> responseBean = new ResponseBean<>();responseBean.setSuccess(true);responseBean.setData(data);return responseBean;}public static <T> ResponseBean<T> error(T errorData) {ResponseBean<T> responseBean = new ResponseBean<>();responseBean.setSuccess(false);responseBean.setData(errorData);return responseBean;}public static <T> ResponseBean<T> success() {ResponseBean<T> responseBean = new ResponseBean<>();responseBean.setSuccess(true);return responseBean;}}
3.結果測試
? ? ? ? 我們就拿剛剛那個b站的長鏈接作測試,即https://www.bilibili.com/video/BV18u4m1K7D4/?spm_id_from=333.1007.tianma.10-4-38.click&vd_source=1c7e32cfbc70017a24ee2c337620ff51
????????
? ? ? ? 可以看到短鏈接生成為1b9590bezhan,
????????然后我們再用這條鏈接去測試能否跳轉到原始鏈接:
????????
? ? ? ? 成功根據短鏈接定向到原始鏈接的網站了。
4.問題
? ? ? ? 1.為什么生成短鏈接需要帶上username參數?
? ? ? ? ?答:這里其實模仿的是不同用戶對應同一個原始鏈接(或長鏈接)時,確保他們生成的短鏈接各不相同,這樣可以方便后臺追蹤是由哪個用戶分享的短鏈接,進而統計分享數。在表中LongUrl和shortUrl的對應關系為一對多