Springboot生成URL短链接和长链接,完整代码实现如下:
1、Controller类
package com.kk.controller;
import com.kk.business.ShortUrlService;
import com.kk.business.UrlModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
/**
* @author kevincui
* @date 2023/8/03 11:47
* @desc: 短链接服务:短链接生成、重定向解析长链接
*/
@RestController
public class ShortUrlController {
@Autowired
private ShortUrlService shortUrlService;
/**
* 编码
* @param urlModel 原始链接实体
* @return 短链接
*/
@ResponseBody
@PostMapping("/smartUrl")
public String smartUrl(@RequestBody UrlModel urlModel) {
if (urlModel == null || StringUtils.isEmpty(urlModel.getUrl()))
return null;
String smartUrl = shortUrlService.encode(urlModel.getUrl());
return smartUrl;
}
/**
* 解码重定向
* @param url 原始链接的编码
* @return 重定向
*/
@GetMapping("/r/{url}")
public ModelAndView redirect(@PathVariable String url) {
String originUrl = shortUrlService.decode(url);
RedirectView redirectView=new RedirectView(originUrl);
// 301永久重定向,避免网络劫持
redirectView.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
return new ModelAndView(redirectView);
}
}
2、实现类
package com.kk.business;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/**
* @author kevincui
* @date 2023/8/03 11:47
* @desc: 短链接编码、解码
*/
@Service
public class ShortUrlService {
/*
* 短链接服务器地址
* */
private String smartUrlDomainName = "http://192.168.0.128:8080";
/*
* 短链接与长链接映射关系集合
* */
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;
}
/**
* 短链接解码码短链接
*
* @param smartUrl 短链接
* @return 原始链接(长链接)
*/
public String decode(String smartUrl) {
// 根据62进制编码(只包含数字、大小写字母)查询原始url信息
// 将62进制转换成十进制,根据主键直接查询
long id = BaseConvertUtil.encode62to10(smartUrl);
String originUrl = urlMap.get(id);
// 初始化默认重定向跳转地址
// 更新浏览次数
return originUrl;
}
}
3、实体类
package com.kk.business;
/**
* @author kevincui
* @date 2023/8/03 11:47
* @description: TODO
*/
public class UrlModel {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
4、工具类
package com.kk.business;
/**
* @author kevincui
* @date 2023/8/03 11:47
* @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;
}
}