我想创建一个URL缩短服务,你可以写一个长URL到输入字段和服务缩短URL为“http://www.example.org/abcdef”。

可以用包含a-z, a-z和0-9的6个字符的字符串代替"abcdef"。这样就有560 ~ 570亿个可能的字符串。

我的方法:

我有一个有三列的数据库表:

Id,整数,自动递增 long, string,用户输入的长URL 短,字符串,缩短的URL(或只有六个字符)

然后将长URL插入到表中。然后,我将为“id”选择自动递增的值,并构建它的散列。这个散列应该作为“short”插入。但是我应该构建什么样的哈希呢?像MD5这样的哈希算法会创建太长的字符串。我认为我不用这些算法。一个自建的算法也可以。

我的想法:

对于“http://www.google.de/”,我得到了自动增量id 239472。然后我执行以下步骤:

short = '';
if divisible by 2, add "a"+the result to short
if divisible by 3, add "b"+the result to short
... until I have divisors for a-z and A-Z.

可以不断重复,直到这个数不能被整除为止。你认为这是一个好方法吗?你有更好的主意吗?

由于对这个主题的持续兴趣,我已经发布了一个高效的GitHub解决方案,包括JavaScript、PHP、Python和Java的实现。如果你喜欢,添加你的解决方案:)


当前回答

对于一个类似的项目,为了获得一个新键,我围绕一个随机字符串生成器创建了一个包装器函数,该函数调用生成器,直到我得到一个尚未在哈希表中使用过的字符串。一旦名称空间开始满了,这个方法就会变慢,但正如您所说的,即使只有6个字符,也有足够的名称空间可以使用。

其他回答

我不断增加数据库中每个域的整数序列,并使用Hashids将整数编码为URL路径。

static hashids = Hashids(salt = "my app rocks", minSize = 6)

我运行了一个脚本,看看它需要多长时间才能耗尽字符长度。对于6个字符,它可以做164,916,224个链接,然后增加到7个字符。Bitly使用7个字符。在我看来,不到五个字很奇怪。

哈希id可以将URL路径解码为整数,但更简单的解决方案是使用整个短链接sho。Rt /ka8ds3作为主键。

以下是完整的概念:

function addDomain(domain) {
    table("domains").insert("domain", domain, "seq", 0)
}

function addURL(domain, longURL) {
    seq = table("domains").where("domain = ?", domain).increment("seq")
    shortURL = domain + "/" + hashids.encode(seq)
    table("links").insert("short", shortURL, "long", longURL)
    return shortURL
}

// GET /:hashcode
function handleRequest(req, res) {
    shortURL = req.host + "/" + req.param("hashcode")
    longURL = table("links").where("short = ?", shortURL).get("long")
    res.redirect(301, longURL)
}

对于一个类似的项目,为了获得一个新键,我围绕一个随机字符串生成器创建了一个包装器函数,该函数调用生成器,直到我得到一个尚未在哈希表中使用过的字符串。一旦名称空间开始满了,这个方法就会变慢,但正如您所说的,即使只有6个字符,也有足够的名称空间可以使用。

我的方法是:获取数据库ID,然后对其进行Base36编码。我不会同时使用大写字母和小写字母,因为这会使通过电话传输这些url成为一场噩梦,但您当然可以轻松地将该功能扩展为62 en/解码器。

为什么要使用哈希呢?

您只需将自动递增值简单地转换为字母数字值。你可以通过使用一些基数转换很容易做到这一点。假设您的字符空间(a-z, a-z, 0-9等)有62个字符,将id转换为以40为基数的数字并使用这些字符作为数字。

Node.js和MongoDB的解决方案

因为我们知道MongoDB用来创建一个12字节的新ObjectId的格式。

一个4字节的值,表示从Unix纪元开始的秒数, 3字节的机器标识符, 2字节的进程id 一个3字节的计数器(在您的机器中),从一个随机值开始。

示例(我选择一个随机序列) a1b2c3d4e5f6g7h8i9j1k2l3

a1b2c3d4表示自Unix纪元以来的秒数, 4e5f6g7表示机器标识符, H8i9表示进程号 J1k2l3表示计数器,从一个随机值开始。

由于计数器是唯一的,如果我们将数据存储在同一台机器上,我们可以毫无疑问地获得它,它将是重复的。

因此,短URL将是计数器,这里是一个代码片段,假设您的服务器运行正常。

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

// Create a schema
const shortUrl = new Schema({
    long_url: { type: String, required: true },
    short_url: { type: String, required: true, unique: true },
  });
const ShortUrl = mongoose.model('ShortUrl', shortUrl);

// The user can request to get a short URL by providing a long URL using a form

app.post('/shorten', function(req ,res){
    // Create a new shortUrl */
    // The submit form has an input with longURL as its name attribute.
    const longUrl = req.body["longURL"];
    const newUrl = ShortUrl({
        long_url : longUrl,
        short_url : "",
    });
    const shortUrl = newUrl._id.toString().slice(-6);
    newUrl.short_url = shortUrl;
    console.log(newUrl);
    newUrl.save(function(err){
        console.log("the new URL is added");
    })
});