springboot 实现长链接转短链接

实现结果: 

转换短链接api:

接口:http://127.0.0.1/api?url=urlencode('要缩短的网址')

例如:http://127.0.0.1/api?url=http%3a%2f%2fwww.baidu.com

返回:http://127.0.0.1/baidu

访问短链接即可还原原url;

转换原理: 将原url通过一系列方式,转换成6位短码(只要能不重复,随便怎么方式都行);将长短链接存入数据库,形成一条对应关系;访问短链接的时候,在数据库找到对应的长链接,并通过重定向实现原url的访问;(如果你的转换方式能过还原,也可以不要数据库,但必须保证转换后的短码不能重复)

核心代码参考 :https://www.cnblogs.com/latteyan/articles/5845117.html

实现步骤:

1.创建数据库(shorturl),创建一个表,存储长链接和与之对应的短链接;

CREATE TABLE `link` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `long_url` varchar(500) DEFAULT NULL COMMENT '长链接',
  `short_url` varchar(255) DEFAULT NULL COMMENT '短链接',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

2.搭建springboot项目,搭建方式网上很多,自行查找;下图是项目构建完成的项目结构(public可以忽略):


3.逻辑层代码依次是:

entity:

package com.tips.entity;

public class Link {

    private Integer id;
    private String longUrl;
    private String shortUrl;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLongUrl() {
        return longUrl;
    }

    public void setLongUrl(String longUrl) {
        this.longUrl = longUrl;
    }

    public String getShortUrl() {
        return shortUrl;
    }

    public void setShortUrl(String shortUrl) {
        this.shortUrl = shortUrl;
    }
}

dao:

package com.tips.mapper;

import com.tips.entity.Link;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface LinkMapper {

    Link selectByPrimaryKey(Integer id);

    int insert(Link link);

    Link findByLongUrl(String longUrl);

    String findByShortUrl(String shortUrl);
}

service:

package com.tips.service;

import com.tips.entity.Link;

public interface LinkService {

    String save(Link link);

    String restoreUrl(String url);

}

service.impl:

package com.tips.service.impl;

import com.tips.entity.Link;
import com.tips.mapper.LinkMapper;
import com.tips.service.LinkService;
import com.tips.util.MD5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class LinkServiceImpl  implements LinkService{

    @Autowired
    private LinkMapper linkMapper;

    /**
     * 转化url
     * 查询数据库是否已经存在 如果存在就返回数据库对应的短链接,如果不存在就查询一条新纪录并返回新的短链接
     * @param link
     * @return
     */
    @Override
    public String save(Link link) {
        String shortUrl = "http://127.0.0.1/";  
        String longUrl = link.getLongUrl();
        System.out.println(longUrl);
        Link link1 = linkMapper.findByLongUrl(longUrl);
        if(link1 == null) {
            shortUrl += this.gererateShortUrl(longUrl);
            link.setShortUrl(shortUrl);
            linkMapper.insert(link);
        }else{
            shortUrl = link1.getShortUrl();
        }
        return shortUrl;
    }

    @Override
    public String restoreUrl(String url) {
        return linkMapper.findByShortUrl(url);
    }

    /**
     * @param args
     */
    public static void main(String[] args) {

        String sLongUrl = "http://474515923.qzone.qq.com" ; //长链接
        LinkServiceImpl link = new LinkServiceImpl();
        String result = link.gererateShortUrl(sLongUrl);
        // 打印出结果
       System.out.println("短链接为: "+ result);
    }

    /**
     * 将长链接转换为短链接
     * @param url
     * @return
     */
    public String gererateShortUrl(String url) {
        // 可以自定义生成 MD5 加密字符传前的混合 KEY
        String key = "caron" ;
        // 要使用生成 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 sMD5EncryptResult = MD5Util.MD5(key+url);
        String hex = sMD5EncryptResult;

//        String[] resUrl = new String[4];
//        for ( int i = 0; i < 4; i++) {

            // 把加密字符按照 8 位一组 16 进制与 0x3FFFFFFF 进行位与运算
            String sTempSubString = hex.substring(2 * 8, 2 * 8 + 8);    //固定取第三组

            // 这里需要使用 long 型来转换,因为 Inteper .parseInt() 只能处理 31 位 , 首位为符号位 , 如果不用 long ,则会越界
            long lHexLong = 0x3FFFFFFF & Long.parseLong (sTempSubString, 16);
            String outChars = "" ;
            for ( int j = 0; j < 6; j++) {
                // 把得到的值与 0x0000003D 进行位与运算,取得字符数组 chars 索引
                long index = 0x0000003D & lHexLong;
                // 把取得的字符相加
                outChars += chars[( int ) index];
                // 每次循环按位右移 5 位
                lHexLong = lHexLong >> 5;
            }
            // 把字符串存入对应索引的输出数组
//            resUrl[i] = outChars;
//        }
        return outChars;
    }
}

util:

package com.tips.util;

import java.security.MessageDigest;


public class MD5Util { 
 // MD5加码。32位 
 public static String MD5(String inStr) { 
  MessageDigest md5 = null; 
  try { 
   md5 = MessageDigest.getInstance("MD5"); 
  } catch (Exception e) { 
   System.out.println(e.toString()); 
   e.printStackTrace(); 
   return ""; 
  } 
  char[] charArray = inStr.toCharArray(); 
  byte[] byteArray = new byte[charArray.length]; 
 
  for (int i = 0; i < charArray.length; i++) 
   byteArray[i] = (byte) charArray[i]; 
 
  byte[] md5Bytes = md5.digest(byteArray); 
 
  StringBuffer hexValue = new StringBuffer(); 
 
  for (int i = 0; i < md5Bytes.length; i++) { 
   int val = ((int) md5Bytes[i]) & 0xff; 
   if (val < 16) 
    hexValue.append("0"); 
   hexValue.append(Integer.toHexString(val)); 
  } 
 
  return hexValue.toString(); 
 } 
 
 // 可逆的加密算法 
 public static String KL(String inStr) { 
  // String s = new String(inStr); 
  char[] a = inStr.toCharArray(); 
  for (int i = 0; i < a.length; i++) { 
   a[i] = (char) (a[i] ^ 't'); 
  } 
  String s = new String(a); 
  return s; 
 } 
 
 // 加密后解密 
 public static String JM(String inStr) { 
  char[] a = inStr.toCharArray(); 
  for (int i = 0; i < a.length; i++) { 
   a[i] = (char) (a[i] ^ 't'); 
  } 
  String k = new String(a); 
  return k; 
 } 
  
 // 测试主函数 
 public static void main(String args[]) { 
  String s = new String("a"); 
  System.out.println("原始:" + s); 
  System.out.println("MD5后:" + MD5(s)); 
  System.out.println("MD5后再加密:" + KL(MD5(s))); 
  System.out.println("解密为MD5后的:" + JM(KL(MD5(s)))); 
  System.out.println("判断是否相同:" + MD5(s).equals(JM(KL(MD5(s))))); 
 } 
} 

mapper:

<?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.tips.mapper.LinkMapper">
    <resultMap id="BaseResultMap" type="com.tips.entity.Link">
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="long_url" jdbcType="VARCHAR" property="longUrl" />
        <result column="short_url" jdbcType="VARCHAR" property="shortUrl" />
    </resultMap>
    <sql id="Base_Column_List">
        id, long_url, short_url
    </sql>

    <!-- 根据长链接查询 -->
    <select id="findByLongUrl" parameterType="java.lang.String" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from link
        where long_url = #{longUrl,jdbcType=VARCHAR}
    </select>
    <!-- 根据短链接查询 -->
    <select id="findByShortUrl" parameterType="java.lang.String" resultType="java.lang.String">
        select long_url from link
        where short_url = #{shortUrl,jdbcType=VARCHAR}
    </select>

    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from link
        where id = #{id,jdbcType=INTEGER}
    </select>

    <insert id="insert" parameterType="com.tips.entity.Link">
        insert into link (id, long_url, short_url)
        values (#{id,jdbcType=INTEGER}, #{longUrl,jdbcType=VARCHAR}, #{shortUrl,jdbcType=VARCHAR})
    </insert>



</mapper>

controller:

package com.tips.controller;

import com.tips.entity.Link;
import com.tips.service.LinkService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
public class LinkController {

    @Autowired
    private LinkService linkService;

    /**
     * 生成短链接
     * @param url
     * @return Caron
     */
    @RequestMapping("/api")
    @ResponseBody
    public Object save(String url){

        if (url == null || "".equals(url)){
            return null;
        }
        if(url.startsWith("http://") || url.startsWith("https://")){
            Link link = new Link();
            link.setLongUrl(url);
            return  linkService.save(link);
        }else{
            return "网址必须以http或https开头";
        }
    }

    /**
     * 301跳转
     * @param url
     * @return
     */
    @RequestMapping("/{url}")
    public String restoreUrl(@PathVariable("url") String url){

        String restoreUrl = linkService.restoreUrl("http://cni.tips/"+url);

        if(restoreUrl != null && !"".equals(restoreUrl)){
            return "redirect:"+restoreUrl;
        }else{
            return "redirect:http://www.cnilink.com";
//            return  "forward:/404.html";    //如果要访问本地html,@RequestMapping("/{url}")前面必须加一层路径/aa/{url}
        }

    }

}

application.properties:


pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com</groupId>
	<artifactId>tips</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>tips</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>



	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

基本所有源码都贴出来了。跑起来试一下呗

  • 4
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值