为什么用短链
有的url会非常长,不利展示也不利于给别人分享,例如以下图片中的url,是不是非常的长
一个比较常见的应案例就是短信,大家看这个图片:
如果短信中的链接使用的是以上那很长的url的话短信费用也会增加,而且用户看的话也很不友好。
如何实现短链服务
其实实现起来并不复杂,我们点开这种短链的时候最后会重定向到真实的url。
创建项目
创建一个项目
nest new short-url
这里使用TypeORM
npm install --save @nestjs/typeorm typeorm mysql2
在app.module.ts中引入TypeOrmModule动态模块并出入相关的配置
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '123123',
database: 'short-url',
entities: [],
synchronize: true,
})
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
我简单说一下里面的配置项目:
entities:[实体类0,实体类1, .....]
, synchronize
如果是true则是同步创建表(entities添加实体类并保存后数据库新增对应的表)。
创建实体类
在src下新建entity/code.ts
。这里面主要存储的是短链。在code.ts中添加如下代码:
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Code {
@PrimaryGeneratedColumn()
id: number;
@Column({
comment: '短链',
})
code: string;
@Column({
comment: '状态, 0 未使用、1 已使用',
})
status: number;
}
接着在entity
里面创建CodeMap.ts,代码如下:
import {
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity()
export class CodeMap {
@PrimaryGeneratedColumn()
id: number;
@Column({
comment: '短链',
})
shortUrl: string;
@Column({
comment: '真正的url',
})
longUrl: string;
@CreateDateColumn()
createTime: Date;
}
将这两个实体添加到TypeOrmModule配置里面:
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '123123',
database: 'short-url',
entities: [Code, CodeMap],
synchronize: true,
}),
执行命令
npm run dev:start
查看数据库就会新增两个表:
生成短链
这里会用到base62这个包
npm install base62
创建一个服务:
nest g s code --no-spec
添加如下代码。这段代码实际上是随机产生len位的字符串。
import * as base62 from 'base62/lib/ascii';
@Injectable()
export class UniqueCodeService {
constructor() {}
generateRandomStr(len: number) {
let str = '';
for (let i = 0; i < len; i++) {
const num = Math.floor(Math.random() * 62);
str += base62.encode(num);
}
return str;
}
}
继续添加向数据插入短链的代码
async generateCode() {
// 获取到6位字符串
const code = this.generateRandomStr(6);
// 向Code表中查找这个生成的code
const res_code = await this.entityManager.findOne(Code, {
where: { code: code },
});
// 如果没有找到生成的code则将这个code插入并将状态设置为0
if (!res_code) {
// 这里返回的是新增的{code, status}
return await this.entityManager.save(Code, {
code: code,
status: 0,
});
} else {
// 如果数据库中有刚刚生产的code那么继续执行这个函数。
return await this.generateCode();
}
}
写到这里又一个问题,数据库里的这些短链是在什么时机存储的呢,其实我们可以在凌晨四点或者其他访问量少的时间执行插入操作,这里就涉及到了定时任务。 首先安装一个包:
npm install --save @nestjs/schedule
添加如下代码。这里的@Cron装饰器里面可以写很多参数,可以是几秒或者小时等,也可以是某个时间点。
// 每天的凌晨四点创建100·00个code
@Cron(CronExpression.EVERY_DAY_AT_4AM)
async batchGenerateCode() {
for (let i = 0; i < 10000; i++) {
await this.generateCode();
}
}
给code(短链)添加对应的url
创建一个服务:
nest g s code-map --no-spec
添加如下代码:
@Injectable()
export class ShortLongMapService {
constructor(
private readonly entityManager: EntityManager,
private readonly uniqueCodeService: UniqueCodeService,
) {}
// 向数据库里添加code相对应的url
async generate(longUrl: string) {
// 首先在Code里面查找status味0的code,也就是这个短链还没对应的url
let uniqueCode = await this.entityManager.findOneBy(Code, {
status: 0,
});
// 如果没有找到,也就是数据库里的code都被添加有相应的url了
if (!uniqueCode) {
// 插入一条code
uniqueCode = await this.uniqueCodeService.generateCode();
}
// 将code和相应的URL插入
await this.entityManager.save(CodeMap, {
shortUrl: uniqueCode.code,
longUrl,
});
// 在Code表里把这个code的status的改成1
await this.entityManager.update(Code, uniqueCode.id, {
status: 1,
});
return uniqueCode.code;
}
}
继续添加获取code相对应的url的函数的代码
async getLongUrl(shortUrl: string) {
const shortLongMap = await this.entityManager.findOneBy(CodeMap, {
shortUrl,
});
return shortLongMap?.longUrl;
}
添加路由,实现重定向
首先添加一个插入url的路由
@Get('short-url')
getHello(@Query('url') url: string) {
return this.shortLongMapService.generate(url);
}
在浏览器输入
接着看数据库中的Code表:
再来看code_map表:
接着添加重定向
@Get(':code')
@Redirect()
async getLongUrl(@Param('code') shortUrl: string) {
const longUrl = await this.shortLongMapService.getLongUrl(shortUrl);
if (!longUrl) throw new HttpException('链接不存在', 404);
return {
url: longUrl,
statusCode: 302,
};
}
在浏览输入刚刚生成的code,会重定向到百度页面
至此一个短链服务就写完了,如果有不对的地方希望大家在评论区留言。🙏