一、为什么需要短网址?
在现代社会,short url已不仅仅是保存空间和排版考虑的问题,更是一种需要在多个媒介传播的新闻、信息、产品宣传、宣传语的一种需求。
而常规的短网址转换基本都是通过跳转的方式实现,虽然看起来简单,但是每一个链接都要跳转一次,跳转次数多了还会给用户带来额外的时间成本,对于大流量网站而言,跳转次数多了会对服务器带来极大的负担,而且有时候并不是用户希望得到的目标页面,这时候就需要一种更有效的方式来实现短网址跳转。
此时就需要有一种更加高效和可靠的实现方式,比如 Base62 编码就是一种非常好的选择。
二、什么是Base62编码?
Base62编码是一种62进制的编码方式,使用数字、大小写字母来表示一个数字。
简单来说,就是将10进制的数字通过转换,用短字符串来表示该数字。可用于生成短网址、生成UID等场景。
Base62编码的值域为0-9a-zA-Z,共计62个字符,由于62的6次方比16的8次方大,所以base62在相同长度情况下能够表示的数字比base16要多得多,而且它的字符串相对长度也要短得多。
三、如何使用Base62编码实现短网址?
首先,我们需要将原始url转换为短码,以便在跳转时使用。一个有效的短网址方案应该包括以下两个环节。
第一步:生成短码:将原始长链接转换为短码。我们可以根据原始URL生成MD5值,然后通过Base62编码生成短码。
// 使用md5可以得到一个128位的哈希值,但使用Base62编码后只需要6位
unsigned char result[16];
md5(input, strlen(input), result);
// 计算生成的短码(使用 Base62 编码),即将 10 进制转换为 62 进制
std::string urlcode = hex2dec(result, sizeof(result));
std::string short_code = encode_base62((int)strtol(urlcode.c_str(), NULL, 16));
第二步:解析短码:请求缩短的短码,解析并有效的301定向到原始的长链接,这是一个经典的请求转发模式。
// 解析 Base62 编码得到 10 进制
unsigned long long count = decode_base62(shortCode);
std::stringstream ss;
ss << std::hex << count;
std::string s = ss.str();
// 补齐位数
std::string md5result(32 - s.length(), '0');
md5result = md5result + s;
// 将 16 进制字符串转换成 unsigned char[]
unsigned char result[16];
for (int i = 0; i < 16; i++)
{
std::string sub = md5result.substr(i * 2, 2);
result[i] = (unsigned char)strtol(sub.c_str(), NULL, 16);
}
// 通过 MD5 解析得到原始 URL
std::string url = md5_to_url(result);
通过以上两个步骤,我们就可以方便地将长链接转换为短链接,从而提高跳转效率。
四、完整代码示例
下面为完整的代码示例,包括了使用MD5生成哈希值以及Base62编码的实现。
#include <iostream>
#include <string>
#include <cstring>
#include <climits>
#include <cmath>
#include <algorithm>
#include <sstream>
const std::string base62_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
// Base62 编码
std::string encode_base62(unsigned long long number)
{
std::string base62_str;
do
{
base62_str.push_back(base62_chars[number % 62]);
number /= 62;
} while (number);
std::reverse(base62_str.begin(), base62_str.end());
return base62_str;
}
// Base62 解码
unsigned long long decode_base62(std::string const& str)
{
unsigned long long number = 0;
for (int i = 0; i < str.size(); ++i)
{
auto pos = base62_chars.find(str[i]);
if (pos == std::string::npos)
return 0;
number += pos * pow(62, str.size() - i - 1);
}
return number;
}
// 将16进制字符串转换成unsigned char[]
unsigned char* hexstr_to_char(const char* source, unsigned char* dest)
{
char h1, h2;
unsigned char* ptr = dest;
while (*source)
{
h1 = toupper(*source++);
if (h1 == 0)
{
*ptr = 0;
return dest;
}
h2 = toupper(*source++);
if (h2 == 0)
{
*ptr = 0;
return dest;
}
if ((h1 >= '0' && h1 <= '9') || (h1 >= 'A' && h1 <= 'F'))
*ptr = (h1 <= '9') ? (h1 - '0') : (h1 - 'A' + 10);
else
break;
*ptr <<= 4;
if ((h2 >= '0' && h2 <= '9') || (h2 >= 'A' && h2 <= 'F'))
*ptr |= (h2 <= '9') ? (h2 - '0') : (h2 - 'A' + 10);
else
break;
ptr++;
}
return dest;
}
// 使用md5计算哈希值
void md5(const char* str, size_t len, unsigned char* result)
{
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
EVP_DigestUpdate(ctx, str, len);
EVP_DigestFinal_ex(ctx, result, NULL);
EVP_MD_CTX_free(ctx);
}
// 将 md5 值转换成 URL
std::string md5_to_url(unsigned char* result)
{
std::stringstream ss;
for (int i = 0; i < 16; i++)
ss << std::hex << (int)result[i];
std::string url = ss.str();
return url.substr(0, 8) + "-" + url.substr(8, 4) + "-" + url.substr(12, 4) + "-" + url.substr(16, 4) + "-" + url.substr(20);
}
int main()
{
// 待转换的 URL
std::string input = "https://www.google.com/";
// 使用 md5 可以得到一个128位的哈希值,但使用 Base62 编码后只需要6位
unsigned char result[16];
md5(input.c_str(), input.length(), result);
// 计算生成的短码(使用 Base62 编码),即将 10 进制转换为 62 进制
std::string urlcode = hex2dec(result, sizeof(result));
std::string short_code = encode_base62((int)strtol(urlcode.c_str(), NULL, 16));
std::cout << "Short URL: " << short_code << std::endl;
// 解析 Base62 编码得到 10 进制
unsigned long long count = decode_base62(short_code);
std::stringstream ss;
ss << std::hex << count;
std::string s = ss.str();
// 补齐位数
std::string md5result(32 - s.length(), '0');
md5result = md5result + s;
// 将 16 进制字符串转换成 unsigned char[]
unsigned char result2[16];
for (int i = 0; i < 16; i++)
{
std::string sub = md5result.substr(i * 2, 2);
result2[i] = (unsigned char)strtol(sub.c_str(), NULL, 16);
}
// 通过 MD5 解析得到原始 URL
std::string url = md5_to_url(result2);
std::cout << "Original URL: " << url << std::endl;
return 0;
}