java realizes long link to short link
We often see short links used in Weibo and text messages, the purpose of which is to simplify redundant long links.
Then I saw a project on code cloud to generate short links: urlshorter: meet the needs of short link generation in various scenarios (gitee.com), and then I modified it on this basis
1. Random string generator
public class SnowFlakeGeneratorRandom implements StringGenerator {<!-- --> @Override public String generate(String url) {<!-- --> SnowFlakeShortUrl snowFlake = new SnowFlakeShortUrl(2, 3); Long id = snowFlake.nextId(); //decimal return NumericConvertUtils.toOtherNumberSystem(id, 62); //62 base } @Override public void setLength(int length) {<!-- -->} }
public class NumericConvertUtils {<!-- --> /** * The set of characters in the hexadecimal representation, 0-Z are used to represent the symbol representation up to 62 hexadecimal */ private static final char[] digits = {<!-- -->'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', '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'}; /** * Convert a decimal number to a string of the specified base * @param number decimal number * @param seed specified base * @return the specified hexadecimal string */ public static String toOtherNumberSystem(long number, int seed) {<!-- --> if (number < 0) {<!-- --> number = ((long) 2 * 0x7fffffff) + number + 2; } char[] buf = new char[32]; int charPos = 32; while ((number / seed) > 0) {<!-- --> buf[--charPos] = digits[(int) (number % seed)]; number /= seed; } buf[--charPos] = digits[(int) (number % seed)]; return new String(buf, charPos, (32 - charPos)); } }
public class SnowFlakeShortUrl {<!-- --> /** * start timestamp */ private final static long START_TIMESTAMP = 1480166465631L; /** * The number of bits occupied by each part */ private final static long SEQUENCE_BIT = 12; //The number of digits occupied by the serial number private final static long MACHINE_BIT = 5; //The number of bits occupied by the machine ID private final static long DATA_CENTER_BIT = 5; //The number of bits occupied by the data center /** * the maximum value of each part */ private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); private final static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_BIT); /** * The displacement of each part to the left */ private final static long MACHINE_LEFT = SEQUENCE_BIT; private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT; private long dataCenterId; //data center private long machineId; //machine ID private long sequence = 0L; //serial number private long lastTimeStamp = -1L; //last time stamp /** * Generate the specified serial number according to the specified data center ID and machine ID * @param dataCenterId data center ID * @param machineId machine ID */ public SnowFlakeShortUrl(long dataCenterId, long machineId) {<!-- --> if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) {<!-- --> throw new IllegalArgumentException("DtaCenterId can't be greater than MAX_DATA_CENTER_NUM or less than 0!"); } if (machineId > MAX_MACHINE_NUM || machineId < 0) {<!-- --> throw new IllegalArgumentException("MachineId can't be greater than MAX_MACHINE_NUM or less than 0!"); } this.dataCenterId = dataCenterId; this.machineId = machineId; } /** * Generate the next ID * @return */ public synchronized long nextId() {<!-- --> long currTimeStamp = getNewTimeStamp(); if (currTimeStamp < lastTimeStamp) {<!-- --> throw new RuntimeException("Clock moved backwards. Refusing to generate id"); } if (currTimeStamp == lastTimeStamp) {<!-- --> //In the same millisecond, the serial number is incremented sequence = (sequence + 1) & MAX_SEQUENCE; //The number of sequences in the same millisecond has reached the maximum if (sequence == 0L) {<!-- --> currTimeStamp = getNextMill(); } } else {<!-- --> //In different milliseconds, the serial number is set to 0 sequence = 0L; } lastTimeStamp = currTimeStamp; return (currTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT //time stamp part | dataCenterId << DATA_CENTER_LEFT //data center part | machineId << MACHINE_LEFT //Machine identification part | sequence; //serial number part } private long getNextMill() {<!-- --> long mill = getNewTimeStamp(); while (mill <= lastTimeStamp) {<!-- --> mill = getNewTimeStamp(); } return mill; } private long getNewTimeStamp() {<!-- --> return System. currentTimeMillis(); } }
2. Storage short link mapping method
public class ShorterStorageMemory<T extends ShorterGetter> implements ShorterStorage<T> {<!-- --> /** * store shorter, url */ private static RedisUtils redisUtils = SpringUtils.getBean(RedisUtils.class); private static final Long expiration_time = 60 * 60 * 24L; public String get(String shorterKey) {<!-- --> Object o = redisUtils. get(shorterKey); if (ObjectUtils. isNotEmpty(o)) {<!-- --> return o + ""; } return null; } public String getMethod(String shorterKey) {<!-- --> Object o = redisUtils. get(shorterKey); if (ObjectUtils. isNotEmpty(o)) {<!-- --> String url = o + ""; Object o1 = redisUtils. get(url); if (ObjectUtils. isNotEmpty(o1)) {<!-- --> return o1.getClass().getName(); } } return null; } public String getMethodClass(String url) {<!-- --> Object o = redisUtils. get(url); if (ObjectUtils. isNotEmpty(o)) {<!-- --> return JsonUtil. ObjectToJson(o); } return null; } public String getShortUrl(String url) {<!-- --> T o = (T) redisUtils. get(url); if (ObjectUtils. isNotEmpty(o)) {<!-- --> return o. getShorter(); } return null; } public void clean(String url) {<!-- --> ShorterGetter shorter = (ShorterGetter) redisUtils.get(url); if (shorter != null) {<!-- --> redisUtils.delete(url); redisUtils.delete(shorter.getShorter()); } } @Override public void cleanShorter(String shorter) {<!-- -->} /** * save map * @param url * @param shorter */ public void save(String url, T shorter) {<!-- --> redisUtils.set(url,shorter,expiration_time); redisUtils.set(shorter.getShorter(),url,expiration_time); } /** * Save the mapping with expiration time * @param url * @param shorter * @param time */ @Override public void save(String url, T shorter, long time) {<!-- --> redisUtils.set(url,shorter,time); redisUtils.set(shorter.getShorter(),url,time); } /** * Short link renewal * @param url * @param shorter */ @Override public void renew(String url, T shorter) {<!-- --> redisUtils.expire(url, expiration_time, TimeUnit.SECONDS); redisUtils.expire(shorter.getShorter(), expiration_time, TimeUnit.SECONDS); } @Override public void clean() {<!-- -->} }
3. Short link service
@Slf4j @RestController @RequestMapping("/") public class UrlController {<!-- --> @Autowired private ShortUrlService shortUrlService; @PostMapping public String createShortUrl(@RequestBody ShortUrlRequest request) {<!-- --> return shortUrlService.generationShortUrl(request.getUrl(),request.getMethod(),1000L,1000L); } @GetMapping("/{shortUrl}") public void url(@PathVariable("shortUrl") String shortUrl,HttpServletResponse response) throws IOException {<!-- --> shortUrlService. getUrl(shortUrl, response); } }
@Slf4j @Service public class ShortUrlService {<!-- --> private static final int urlLen = 6; private static final String HTTP_PREFIX = "http://127.0.0.1:8088/"; @Autowired private RedisUtils redisUtils; /** * Generate short link * * @param url * @return */ public String generationShortUrl(String url, String method, Long period, Long time) {<!-- --> if (redisUtils.hasKey(url)) {<!-- --> ShorterStorageMemory storageMemory = new ShorterStorageMemory(); return HTTP_PREFIX + storageMemory.getShortUrl(url); } GeneratorShortUrlMethodEnum generatorShortUrlMethodEnum = GeneratorShortUrlMethodEnum.getShortUrlMethodEnum(method); if (generatorShortUrlMethodEnum == null) {<!-- --> throw new RuntimeException("The method parameter is wrong"); } String shortUrl = ""; switch (generatorShortUrlMethodEnum) {<!-- --> case SHORT_URL_LIMIT: UrlShorterGeneratorSimple simple = getUrlShorterGeneratorSimple(); shortUrl = simple. generate(url). getShorter(); break; case SHORT_URL_LIMIT_PERIOD: UrlShorterGeneratorLimitPeriod limitPeriod = getUrlShorterGeneratorLimitPeriod(period); shortUrl = limitPeriod.generate(url).getShorter(); break; case SHORT_URL_LIMIT_TIME: UrlShorterGeneratorLimitTimes limitTimes = getUrlShorterGeneratorLimitTimes(time); shortUrl = limitTimes. generate(url). getShorter(); case SHORT_URL_LIMIT_PERIOD_TIME: UrlShorterGeneratorLimitPeriodAndTimes periodAndTimes = getUrlShorterGeneratorLimitPeriodAndTimes(period, time); shortUrl = periodAndTimes. generate(url). getShorter(); case SHORT_URL_LIMIT_PASSWORD: UrlShorterGeneratorWithPassword withPassword = getUrlShorterGeneratorWithPassword(); shortUrl = withPassword.generate(url).getShorter() + "." + withPassword.generate(url).getPassword(); default: throw new RuntimeException(); } return HTTP_PREFIX + shortUrl; } /** * Get the original link according to the short link * * @param shortUrl * @return */ public void getUrl(String shortUrl, HttpServletResponse response) {<!-- --> try {<!-- --> ShorterStorageMemory shorterStringShorterStorageMemory = new ShorterStorageMemory<>(); String url = shorterStringShorterStorageMemory.get(shortUrl); if (StringUtils.isBlank(url)) {<!-- --> throw new RuntimeException("The link has expired"); } String method = shorterStringShorterStorageMemory.getMethod(shortUrl); String key = shortUrl + "_increment"; if (method. contains("ShorterString")) {<!-- --> ShorterString shorterString = JsonUtil.JsonToObject(shorterStringShorterStorageMemory.getMethodClass(url), ShorterString.class); shorterStringShorterStorageMemory.renew(url,shorterString); } else if (method. contains("ShorterWithPassword")) {<!-- --> ShorterWithPassword shorterWithPassword = JsonUtil.JsonToObject(shorterStringShorterStorageMemory.getMethodClass(url), ShorterWithPassword.class); } else if (method. contains("ShorterWithPeriod")) {<!-- --> ShorterWithPeriod shorterWithPeriod = JsonUtil.JsonToObject(shorterStringShorterStorageMemory.getMethodClass(url), ShorterWithPeriod.class); shorterStringShorterStorageMemory.renew(url,shorterWithPeriod); limitCount(shorterStringShorterStorageMemory, url, key, shorterWithPeriod.getPeriod()); } else if (method. contains("ShorterWithPeriodAndTimes")) {<!-- --> ShorterWithPeriodAndTimes shorterWithPeriodAndTimes = JsonUtil.JsonToObject(shorterStringShorterStorageMemory.getMethodClass(url), ShorterWithPeriodAndTimes.class); limitCount(shorterStringShorterStorageMemory,url,key,shorterWithPeriodAndTimes.getPeriod()); } else if (method. contains("ShorterWithTimes")) {<!-- --> ShorterWithTimes shorterWithTimes = JsonUtil.JsonToObject(shorterStringShorterStorageMemory.getMethodClass(url), ShorterWithTimes.class); } if (ObjectUtils.isNotEmpty(url) & amp; & amp; StringUtils.isNotBlank(method)) {<!-- --> response. sendRedirect(url); } } catch (Exception e) {<!-- --> log.error("Failed to get the short link: {}", e.getMessage()); throw new RuntimeException("The link has expired"); } } public void limitCount(ShorterStorageMemory memory, String url, String key, long count) {<!-- --> if (redisUtils.hasKey(key)) {<!-- --> int limit = Integer.parseInt(redisUtils.get(key) + ""); if (limit > count) {<!-- --> memory.clean(url); redisUtils.delete(key); throw new RuntimeException("The link has expired"); } redisUtils.incrBy(key, 1L); } else {<!-- --> redisUtils.set(key, 1); } } private UrlShorterGeneratorSimple getUrlShorterGeneratorSimple() {<!-- --> UrlShorterGeneratorSimple simple = new UrlShorterGeneratorSimple(); simple. setGenerator(new SnowFlakeGeneratorRandom()); simple.setShorterStorage(new ShorterStorageMemory<ShorterString>()); return simple; } private UrlShorterGeneratorLimitPeriod getUrlShorterGeneratorLimitPeriod(long count) {<!-- --> UrlShorterGeneratorLimitPeriod limitPeriod = new UrlShorterGeneratorLimitPeriod(); limitPeriod.setGenerator(new SnowFlakeGeneratorRandom()); limitPeriod. setPeriod(count); limitPeriod.setShorterStorage(new ShorterStorageMemory<ShorterWithPeriod>()); return limitPeriod; } private UrlShorterGeneratorLimitPeriodAndTimes getUrlShorterGeneratorLimitPeriodAndTimes(long count, long time) {<!-- --> UrlShorterGeneratorLimitPeriodAndTimes limitPeriodAndTimes = new UrlShorterGeneratorLimitPeriodAndTimes(); limitPeriodAndTimes.setGenerator(new SnowFlakeGeneratorRandom()); limitPeriodAndTimes. setPeriod(count); limitPeriodAndTimes.setTimes(time); limitPeriodAndTimes.setShorterStorage(new ShorterStorageMemory<ShorterWithPeriodAndTimes>()); return limitPeriodAndTimes; } private UrlShorterGeneratorLimitTimes getUrlShorterGeneratorLimitTimes(long time) {<!-- --> UrlShorterGeneratorLimitTimes limitTimes = new UrlShorterGeneratorLimitTimes(); limitTimes.setGenerator(new SnowFlakeGeneratorRandom()); limitTimes. setTimes(time); limitTimes.setShorterStorage(new ShorterStorageMemory<ShorterWithTimes>()); return limitTimes; } private UrlShorterGeneratorWithPassword getUrlShorterGeneratorWithPassword() {<!-- --> UrlShorterGeneratorWithPassword withPassword = new UrlShorterGeneratorWithPassword(); withPassword.setShorterGenerator(new SnowFlakeGeneratorRandom()); withPassword.setPasswordGenerator(new StringGeneratorRandom()); withPassword.setShorterStorage(new ShorterStorageMemory<ShorterWithPassword>()); return withPassword; } }
Fourth, other categories
@Getter @NoArgsConstructor @AllArgsConstructor public enum GeneratorShortUrlMethodEnum {<!-- --> SHORT_URL_LIMIT_PERIOD("limit.period","Used to generate a short link to limit the number of visits"), SHORT_URL_LIMIT_PERIOD_TIME("limit.period.time","Used to generate a short link that limits the number of visits and effective time"), SHORT_URL_LIMIT_TIME("limit.time","Used to generate a short link for the effective time limit"), SHORT_URL_LIMIT("limit","for short link"), SHORT_URL_LIMIT_PASSWORD("limit.password","used to generate a short link with password"); private String method; private String methodDesc; public static GeneratorShortUrlMethodEnum getShortUrlMethodEnum(String method) {<!-- --> if (StringUtils.isBlank(method)) {<!-- --> return null; } for (GeneratorShortUrlMethodEnum s : GeneratorShortUrlMethodEnum.values()) {<!-- --> if (method. equals(s. getMethod())) {<!-- --> return s; } } return null; } }
V. Description
1. If no expiration time is specified, the default expiration time is 24 hours
2. Redis is used to save the short link mapping this time. Of course, mysql can also be used.
3. The prefix of the generated short link is the domain name of your own server, so that you can jump directly by entering the short link on the web page
Go to the original link, and you can also record the number of visits to the short link;
Note: Some classes of methods here are not written, you can go to the link of the above author to see