How to convert long link to short link in java

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