专栏名称: 芋道源码
纯 Java 源码分享公众号,目前有「Dubbo」「SpringCloud」「Java 并发」「RocketMQ」「Sharding-JDBC」「MyCAT」「Elastic-Job」「SkyWalking」「Spring」等等
目录
相关文章推荐
芋道源码  ·  12月跳槽的兄弟注意了。。 ·  昨天  
芋道源码  ·  听说 GitHub 上阅读源码很费劲? ·  2 天前  
JavaGuide  ·  Nacos 3.0 考虑丢弃 Spring ... ·  3 天前  
芋道源码  ·  详解企业级数据脱敏方案 ·  4 天前  
51好读  ›  专栏  ›  芋道源码

难道你还在为短链跳转烦恼?SpringBoot轻松搞定!

芋道源码  · 公众号  · Java  · 2024-12-16 09:30

正文

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入芋道快速开发平台知识星球。下面是星球提供的部分资料: 

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、CRM 等等功能:

  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 双版本 

来源:blog.csdn.net/Kristabo
/article/details/136369393


1.背景介绍

短链跳转是一种通过将长链接转换为短链接的方式,以便在互联网上进行链接共享和传播的技术。通常情况下,长链接可能由于包含大量参数或者较长的路径而显得复杂且不易记忆,而短链则是将原始长链接通过特定算法转换为较短的链接,使得它更容易分享、传播和展示。

短链跳转服务通常由第三方提供,用户可以将需要缩短的长链接提交到该服务,服务会返回一个短链接,当用户访问这个短链接时,会被重定向到原始的长链接地址。这种服务通常还提供了统计功能,可以跟踪短链接被点击的次数、访问来源等信息,帮助用户了解链接的传播效果。

短链跳转服务有助于美化链接、节省空间、方便分享和统计链接访问情况,因此被广泛应用于社交媒体、微博客、推广活动等各种互联网应用场景中。

比如在b站中,一个视频的网址原来是这样的:

在移动端中,点击分享按钮,复制其链接:

它会变成如下链接形式:

【Cookie、Session、Token、JWT一次性讲完-哔哩哔哩】 https://b3.tv/0SMtY

点击该链接后,你会发现浏览器的网址URL为原来的长链接形式,也就是说这其中发生了重定向 ,而这个过程就是这篇博客要提到的短链跳转了。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

2.短链跳转的意义

  • 节省空间: 长链接可能会很长,不方便分享或展示,通过短链跳转可以将长链接转换为短链接,节省字符空间。
  • 美化链接: 短链看起来更简洁、美观,对于需要展示给用户或发布到社交媒体等场景更具吸引力。
  • 防止链接失效: 某些长链接可能会因为过期、失效或变动而无法访问,通过短链跳转可以在后台进行管理和更新,保证链接的可访问性。
  • 统计和跟踪: 通过短链跳转服务可以对链接的点击量、来源、地域等信息进行统计和分析,帮助用户了解链接的受众和效果。
  • 方便分享: 短链更容易复制、粘贴和分享,适用于短信、微博、邮件等分享场景,提高分享效率。
  • 隐藏原始链接: 有时候希望隐藏原始链接的信息,通过短链跳转可以起到一定的保护作用,防止泄露敏感信息。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

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.projectlombokgroupId>
    <artifactId>lombokartifactId>
    <optional>trueoptional>
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映射:


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_map
        where longUrl =#{longUrl} and username = #{username}
        limit 1
    select>
 
    
    <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_map
        where 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 {
 
    @Resource
    private 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(08)+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 {
 
    @Resource
    private 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  ResponseBean success(T data) {
        ResponseBean responseBean = new ResponseBean<>();
        responseBean.setSuccess(true);
        responseBean.setData(data);
        return responseBean;
    }
 
    public static  ResponseBean error(T errorData) {
        ResponseBean responseBean = new ResponseBean<>();
        responseBean.setSuccess(false);
        responseBean.setData(errorData);
        return responseBean;
    }
 
    public static  ResponseBean success() {
        ResponseBean responseBean = new ResponseBean<>();
        responseBean.setSuccess(true);
        return responseBean;
    }
 
}

4.结果测试

我们就拿刚刚那个b站的长链接作测试,即https://www.bilibili.com/video/BV18u4m1K7D4/?spm_id_from=333.1007.tianma.10-4-38.click&vd_source=1c7e32cfbc70017a24ee2c337620ff51

可以看到短链接生成为1b9590bezhan,

然后我们再用这条链接去测试能否跳转到原始链接:

成功根据短链接定向到原始链接的网站了。

5.问题

为什么生成短链接需要带上username参数?

答:这里其实模仿的是不同用户对应同一个原始链接(或长链接)时,确保他们生成的短链接各不相同,这样可以方便后台追踪是由哪个用户分享的短链接,进而统计分享数。在表中LongUrl和shortUrl的对应关系为一对多


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。

谢谢支持哟 (*^__^*)