导语
本文是将如何搭建一个功能全面的web服务端作为目的,通过列出一个完整服务端所需功能,针对这些功能一步步思考并搭建的过程。其中很多想法也是来自工作中与后端打交道的经验。
本文不是手把手教写所有代码,很多知识点更推荐大家翻阅官网文档(例如sql语法,nest语法等等)。本文是重在以前端视角探索后端,给大家一个后端开发的启蒙(前端不是切图仔,后端也不是sql boy)
如果大家发现哪些功能有遗漏,或者有更好的方案,也可以提出来讨论~
本文会长期更新
本文代码仓库地址 github.com/wangziweng7…
功能列表
- 合理的目录结构规范、命名规范
- 接口请求处理
- 处理
resful请求(get、post、put、delete等) - 请求参数处理(
url query、url params、form-urlencode、json等) - 参数校验和转换
- 处理
- 接口响应处理
- 统一的返回响应格式
- 状态码规范
- 流式数据
- 结构化数据存储(
sqlite、mysql、prismaortypeorm)- orm操作
- 数据迁移
- 实体关系图
- 对象存储服务(
阿里oss)- 文件安全
- 分片
- cdn加速
- 版本控制
- 图片处理
- 日志
- 记录和监视所有进出服务器 的请求,包括请求的来源、时间、响应数据等。用于故障排除、性能调优和安全审计等目的(
winston,es) - 过滤敏感数据
- 记录和监视所有进出服务器 的请求,包括请求的来源、时间、响应数据等。用于故障排除、性能调优和安全审计等目的(
- 性能
- api文档生成(swagger、apifox)
- 消息队列(rabbit、Kafka)
- 定时任务
- 负载均衡(kong, apisix, nginx)
- 远程调用(
http、grpc、tcp) - 配置中心
- ETCD
- 监控
- 会话管理
- 能够管理用户与服务器的会话状态(
jwt,oauth2)
- 能够管理用户与服务器的会话状态(
- 权限管理
- 根据用户权限来进行有限的操作
一.目录规范,命名规范
因为技术选型是基于nestjs,所以首先是在github上面找开源的多星的项目,以他们的架构为基础,搭建自己的项目。
1.目录结构
+-- bin // 自定义脚本
+-- dist // 打包生成目录
+-- public // 静态文件
+-- src
| +-- config // 环境变量
| +-- base // 公共业务
| +-- common // 公共与业务无关
| | +-- constants // 常量 和 Enum
| | +-- controllers // Nest Controllers
| | +-- decorators // Nest Decorators
| | +-- dto // DTO (Data Transfer Object) Schema, Validation
| | +-- filters // Nest Filters
| | +-- guards // Nest Guards
| | +-- interceptors // Nest Interceptors
| | +-- interfaces // TypeScript Interfaces
| | +-- middleware // Nest Middleware
| | +-- pipes // Nest Pipes
| | +-- providers // Nest Providers
| | +-- * // models, repositories, services...
| +-- shared // Shared Nest Modules
| +-- * // 其它
+-- test // Jest testing
+-- typings // Modules and global type 定义
// Module structure
//业务代码如下
+-- src/greeter
| +-- * // folders
| +-- greeter.constant.ts
| +-- greeter.controller.ts
| +-- greeter.service.ts
| +-- greeter.module.ts
| +-- greeter.*.ts
2.文件命名&类命名
export class PascalCaseSuffix {} //= pascal-case.suffix.ts
// Except for suffix, PascalCase to hyphen-case
class FooBarNaming {} //= foo-bar.naming.ts
class FooController {} //= foo.controller.ts
class BarQueryDto {} //= bar-query.dto.ts
3.Interface 命名
interface User {}
interface CustomeUser extends User {}
interface ThirdCustomeUser extends CustomeUser {}
4.新建项目
确定结构之后,我们接下来开始新建一个nest项目
1.安装nestjs并创建我们的项目
npm i -g @nestjs/cli
nest new project-name
pnpm install
2.运行
npm run start:dev
二.接口请求处理
这一节我们将完成一个restful(get、post、put、delete)类型接口,同时支持请求参数的获取(url query、url params、form-urlencode、json)与校验
主要内容对照官网文档即可控制器 | NestJS 中文网 (nodejs.cn)
以用户注册,登录,登出,修改密码作为场景
1.使用命令创建模块相关
nest g resource base/auth
生成以下代码
这里我们主要用到三个文件
controller分发路由,验证请求参数service具体的业务逻辑处理dto参数类型,用来实例化参数类和验证参数
其它几个文件,我们在数据库的时候再用
2.参数校验还需要下面的依赖
pnpm i class-transformer class-validator
3.我们先编写controller来处理请求
auth.controller.ts
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
import { AuthService } from './auth.service';
import { CreateAuthDto, UpdateAuthDto } from './dto/auth.dto';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) { }
// 注册
// @Body同时能处理form-urlencode和json格式
@Post('register')
register(@Body() createAuthDto: CreateAuthDto) {
return this.authService.register(createAuthDto);
}
// 登录
@Post('login')
login(@Body() createAuthDto: CreateAuthDto) {
return this.authService.login(createAuthDto);
}
//登出
@Post('logout')
logout(@Body() createAuthDto: CreateAuthDto) {
return this.authService.logout(createAuthDto);
}
// 获取用户信息
@Get('getUser')
findOne(@Query('id') id: string) {
return this.authService.findOne(+id);
}
// 修改用户信息
@Patch(':id')
update(@Param('id') id: string, @Body() updateAuthDto: UpdateAuthDto) {
return this.authService.update(+id, updateAuthDto);
}
}
4.dto来声明参数类型和校验
auth.dto.ts
import { IsNotEmpty, MinLength, MaxLength } from 'class-validator';
export class CreateAuthDto {
@IsNotEmpty()
@MinLength(3)
@MaxLength(20)
username: string;
@IsNotEmpty()
@MinLength(6)
@MaxLength(20)
password: string;
}
export class UpdateAuthDto {
@IsNotEmpty()
@MinLength(3)
@MaxLength(20)
password: string;
}
5.同时还需要在全局开启参数类转换为实例
main.ts
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({
transform: true, // 转换属性,
whitelist: true, // 去除未声明的属性
}
));
await app.listen(3000);
}
下面用postman发请求验证
参数错误时,返回了相关错误提示
参数正确时,返回响应
小结
- 用nest自带的Get、Post、Patch等装饰器处理不同类型的请求
- 用Query、Body、Param装饰器去接收了不同的请求参数,
- 用ValidationPipe、class-validator、class-transformer做了参数的自动转换和校验
nest还有很多其它请求相关装饰器,需要的时候在去查文档就行
二.接口响应处理
从上面POSTMAN请求返回的响应结果可以看到,我们的数据结构并不统一,这样前端会很难判断什么时候成功,什么时候失败,什么时候需要重新登录,请求超时,从哪里拿返回数据等
所以统一的返回响应格式,加状态码区分状态对我们来说非常重要
1.首先定义数据结构
请求正确时
import { HttpStatus, StreamableFile } from '@nestjs/common';
// 普通返回类型
type NormalDataType = {
message: string
statusCode: HttpStatus
data?: string | number | Array<any> | Record<string, any>
}
// 流式返回类型
type StreamType = StreamableFile
// 同理我们还可以加上graphql等类型,然后针对不同类型定义响应结果
// 具体代码略,后面需要时在加上来
type ResponseType = NormalDataType | StreamableFile
请求错误时
type ErrorResponseType = {
message: Array<string>
error?: string
statusCode: HttpStatus
}
2.接着新建拦截器处理正确请求
nest g interceptor common/interceptor/response-format
import { CallHandler, ExecutionContext, Injectable, NestInterceptor, HttpStatus } from '@nestjs/common';
import { Observable, map } from 'rxjs';
// 普通返回类型
export type NormalDataType = {
message: string
statusCode: HttpStatus
data?: string | number | Array<any> | Record<string, any>
}
// 流式返回类型
export type StreamType = StreamableFile
// 同理我们还可以加上graphql等类型,然后针对不同类型定义响应结果
// 具体代码略,后面需要时在加上来
export type ResponseType = NormalDataType | StreamableFile
@Injectable()
export class ResponseFormatInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<ResponseType> {
return next.handle().pipe(map(data => {
// 流式就直接返回
if (data instanceof StreamableFile) return data
return {
statusCode: HttpStatus.OK,
message: 'success',
data
}
}))
}
}
3.在app.module.ts通过APP_INTERCEPTOR全局使用该拦截器
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { ResponseFormatInterceptor } from './common/interceptor/response-format/response-format.interceptor';
// 略
@Module({
// 略
providers: [
{
provide: APP_INTERCEPTOR,
useClass: ResponseFormatInterceptor,
}
// 略
],
})
export class AppModule {}
最终验证
1.正常请求格式验证通过
2.请求错误验证
让业务代码手动抛错
import { HttpException, HttpStatus } from '@nestjs/common';
// 略
@Post('register')
register(@Body() createAuthDto: CreateAuthDto) {
throw new HttpException('没有权限', HttpStatus.FORBIDDEN);
}
错误请求 验证成功(请求错误nestjs自动处理了格式,不需要我们处理)
3.构造验证流式数据
import { createReadStream } from 'fs';
import { join } from 'path';
import { StreamableFile } from '@nestjs/common';
@Get('file')
getFile(): StreamableFile {
const file = createReadStream(join(process.cwd(), 'src/main.ts'));
return new StreamableFile(file);
}
流式数据验证通过
小结
- 通过
APP_INTERCEPTOR注册全局拦截器实现了响应对象的格式化 - 通过内置
httpstatus枚举与HttpException来返回不同类型的错误或者格式 - 通过
StreamableFile来实现了流式响应 - 对于graphql等其它响应我们后面在详细说明
三.结构化数据存储
在上面的例子中,我们注册的用户并没有存储,会反复注册。所以我们需要一个数据库来保存用户。
数据库选型
数据库的选型很多,下面以sqlite为例方便演示。
sqlite和其它数据库它们的语法在很大程度上是相同的,所以用它来练习非常方便,它不用去搭数据库服务,因为sqlite是以文件为库,存储在本地,非常轻量级。
(如果你想接入其它数据库,看文档就行数据库 | NestJS 中文网 (nodejs.cn))
为什么用orm框架
大部分后端服务都不会直接写sql,因为很容易发生sql注入等安全问题,基本都是用orm框架,以对象形式来操作sql。
数据库ORM也有很多,这里笔者分别使用了typeorm和prisma。
下面也将体验两者的优缺点。
为什么要用ORM的数据迁移来生成sql
- 手动写容易写错
- 非敏捷迭代下,一个项目周期可能是几个月,这几个月我们陆陆续续执行了很多表结构的修改,如果没有一个统一的地方保存这些结构sql,最后很容易发生测试和生产环境数据表结构不一致问题(某某的sql又忘记执行了)
用orm框架来生成迁移sql并保存,最后统一放到sql审计平台执行能减少流程上的错误
为什么数据库表ER图很重要
清晰方便的查看表关系
所以综上来说,ORM框架对我们来说非常重要,在项目启动阶段的选型会影响我们后续的开发体验
typeorm
1.首先安装所需依赖
pnpm install @nestjs/typeorm typeorm sqlite3
2.将 TypeOrmModule 导入到根 AppModule 中
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
database: 'db/database.sqlite', // 指定数据库地址
entities: [],
busyErrorRetry: 5,
enableWAL: true,
autoLoadEntities: true,
synchronize: true, // 自动根据entities建表. true in development, false in production,
logging: true, // 打印sql语句. true in development, false in production,
}),
],
})
export class AppModule {}
3.新建数据库实体
这时我们用上之前没用到的auth.entity.ts文件,在里面新建一个类。当我们在第二步的配置里开启了synchronize后,nest启动时便会根据实体类为我们自动建表。(生产环境一般是在sql审计平台执行sql,这里只是为了开发方便才开启)
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column('varchar', { nullable: false, length: 255 })
username: string;
@Column('varchar', { nullable: false, length: 255 })
password: string;
@Column('datetime', { nullable: false, transformer: { to: () => new Date().getTime(), from: (val) => val } })
updated_at: string;
@Column('datetime', { nullable: false, transformer: { to: (val) => val ?? new Date().getTime(), from: (val) => val } })
created_at: string;
}
4.在auth.modules.ts 中注册该实体
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/auth.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule {}
5.最后我们修改我们的auth.service.ts
通过userRepository来操作数据库user表
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { CreateAuthDto } from './dto/auth.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/auth.entity';
@Injectable()
export class AuthService {
@InjectRepository(User)
private readonly userRepository: Repository<User>
async register(createAuthDto: CreateAuthDto) {
const hasUser = await this.userRepository.existsBy({
username: createAuthDto.username,
})
if (hasUser) {
throw new HttpException('用户名已存在', HttpStatus.BAD_REQUEST)
}
await this.userRepository.save(createAuthDto)
return '创建成功'
}
}
6.我们使用postman来发请求和NavicatforSqlite来可视化查看数据库
www.navicat.com.cn/download/di… 下载地址
同时查看数据库成功带保存了我们的用户信息
我们在重新请求一下,发现重复注册失败,因为已有该用户名
迁移功能
过程略,直接说结论了
Typeorm使用对比本地实体与数据库的差异生成迁移sql,这点对我来说觉得很不友好
- 操作麻烦
- 一般生产环境不会给你账号密码,所以不会对比生产。
- 用测试环境去生成时,可能多人并行开发的情况下,帮别人的也生成了,别人又帮你的部分生成了,有心智负担。也可能测试环境某人手动操作了,导致有污染。
小结
- 通过
TypeOrmModule.forRoot建立数据库链接 - 通过
entity装饰器修饰的实体类建表,并TypeOrmModule.forFeature实例化该表对象方便操作 - 通过
InjectRepository装饰器在我们服务中拿到了该类实例操作数据
TypeOrmModule.forRoot可以注册多次,建立多个数据库的链接,但是需要为每个数据源建立name字段。通过TypeOrmModule.forFeature([实体类], 'name')实例化,
InjectRepository(实体类, 'name')来使用。和单体数据源使用几乎没啥区别。下面就不演示了
优点:
- 用类的写法定义表,可以很方便的处理数据的转换和生成默认数据,例如我们自动生成创建时间和更新时间,而不用业务代码去处理
- 代码提示友好
缺点:
- 写法麻烦,使用一个表要要在两个文件导四次包。
- 数据库迁移麻烦
prisma
下面我们演示prisma
1.安装&初始化
pnpm install prisma nestjs-prisma @prisma/client
接着vscode商店搜索prisma语法提示及代码高亮插件
然后初始化文件
npx prisma init
此命令创建一个新的 prisma 目录,其中包含以下内容:
schema.prisma:指定你的数据库连接并包含数据库架构.env:dotenv 文件,通常用于将你的数据库凭据存储在一组环境变量中
2.设置数据库连接
schema.prisma
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
relationMode = "prisma"
}
generator client {
provider = "prisma-client-js"
}
其中relationMode = "prisma" 代表生成sql时并不会生成真正的外键关系,这个在生成表关系图时很有用
.env
DATABASE_URL="file:./db/database.sqlite"
使用.env文件我们需要依赖用ConfigModule
pnpm i @nestjs/config
在app.module.ts中引入
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [ConfigModule.forRoot()],
})
3.在schema.prisma新建数据表
model User {
id String @id @default(uuid())
username String @unique
password String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
posts Post[]
}
model Post {
id Int @default(autoincrement()) @id
title String
content String?
published Boolean? @default(false)
author User? @relation(fields: [authorId], references: [id])
authorId String?
@@index([authorId])
}
业务使用
1.在跟模块app.modules.ts引入
import { PrismaModule, loggingMiddleware } from 'nestjs-prisma';
import { Module, Logger } from '@nestjs/common';
imports: [
ConfigModule.forRoot(),
PrismaModule.forRoot({
isGlobal: true,
prismaServiceOptions: {
middlewares: [
// configure your prisma middleware
loggingMiddleware({
logger: new Logger('PrismaMiddleware'),
logLevel: 'log',
}),
],
},
})
],
2.在业务代码中使用
auth.service.ts
import { PrismaService } from 'nestjs-prisma';
@Injectable()
export class AuthService {
constructor(private prisma: PrismaService) {}
async register(createAuthDto: CreateAuthDto) {
const hasUser = await this.prisma.user.findUnique({
where: { username: createAuthDto.username }
})
if (hasUser) {
throw new HttpException('用户名已存在', HttpStatus.BAD_REQUEST)
}
await this.prisma.user.create({ data: createAuthDto })
return '创建成功'
}
}
3.postman调用,然后查看数据库
迁移功能
1.执行命令
npx prisma migrate dev --name init
生成如下代码
2.重复执行命令
不会生成新的迁移sql,prisma会提示没有修改
3.新增字段在执行
当我们给Post表新增一个star字段在执行
npx prisma migrate dev --name add
生成了新的迁移sql
prisma的迁移功能是根据已有migration和model之间的差异来生成,比typeorm的对比方式更合理
实体关系图
1.首先安装所需依赖
pnpm i -D prisma-erd-generator @mermaid-js/mermaid-cli
2.然后执行生成命令
npx prisma generate
3.最后查看效果
小结
优点:使用非常方便,代码提示也很友好,文档全面,功能也非常多缺点:没有发现有typeorm那样的transformer来处理表字段的转换,只能使用内置关键字
所以综上,笔者更推荐prisma来代替typeorm
四.文件存储服务
文件存储与文件下载是web服务端常见的需求,一般我们不会用本地服务器来存储文件,而是会把文件上传到专门的OSS (Object Storage Service)对象存储服务,比起存到自己服务器,它主要有以下几个特点。
- 方便以后的资源扩展
- CDN加速访问
- 定时备份
- 文件的版本控制
安全性- ...
其中安全性是需要我们特别关注的。安全性需要我们和云服务共同维护。
那么我们自己该怎么做?
其实阿里给出了解决方案
概况总结如下
通过创建子用户,对子用户授权- 如果子用户泄露,可以立马禁用子用户减少损失。
子用户在服务端对客户端授权- 对于前端层面,使用临时凭证来上传,防止前端泄漏,造成数据安全。
下面我们以阿里OSS为例进行操作,进行实战
- 开通阿里OSS服务
- 创建2个Bucket
- 一个用于用户数据(安全要求高,每次读需要鉴权)
- 一个用于网站静态数据(无安全要求,读不需要鉴权)
- 代码相关
开通服务
首先我们进入阿里云官网进行OSS服务购买 阿里云-搜索推荐页 (aliyun.com)
1.我们先购买半年OSS存储服务(5元半年40GB)
- 接着
点击管理控制台
3.接着点击立即开通
4.接着点击立即购买
5.完成购买后,我们点击对象存储OSS管理控制台
创建bucket,以及安全配置
6.进入Bucket列表,新建一个bucket
选择快捷创建(默认所有文件操作需要进行鉴权),我们只需要填写bucket名称和选择地域即可(随便填无所谓)
8.创建角色&授权
总共三个权限如下
9.创建用户&授权
10.全部配置好后,我们在确定文件上传方式,选择客户端直传、服务端生产STS临时访问凭证
- 拿到我们所需要的所有参数
interface OssType {
accessKeyId: string
accessKeySecret: string
roleArn: string
bucket: string
region: string
}
其中bucket和region从bucket域名里面拿,以.分割,第一个是bucket:"my-bucket-wzw",第二个是region: "oss-cn-shenzhen"
roleArn 从角色里面拿
accessKeyId和accessKeySecret从用户里拿
代码
配置都准备好后,可以开始进行代码编辑
1.安装相关依赖
pnpm i ali-oss @types/ali-oss @nestjs/schedule
2.准备配置文件
src\config\configuration.ts
export default () => ({
oss: {
accessKeyId: 'xxxx', // 参数需要替换为你自己的oss配置
accessKeySecret: 'xxxx',
roleArn: 'xxxxx',
bucket: 'xxxx',
region: 'xxxxx'
}
});
3. app.modules.ts进行全局注册配置
同时引入定时模块,方便获取临时凭证
import { ConfigModule } from '@nestjs/config';
import configuration from './config/configuration';
import { ScheduleModule } from '@nestjs/schedule';
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [configuration], // 会和.env进行合并
}),
ScheduleModule.forRoot(), // 定时任务
]
4.创建我们的oss模块
nest g resource base/oss
这里我们只需要重点关注service和controller
oss.controller.ts
import { Controller, Get, Query } from '@nestjs/common';
import { OssService } from './oss.service';
@Controller('oss')
export class OssController {
constructor(private readonly ossService: OssService) {}
@Get('sts_token')
getStsToken() {
return this.ossService.getStsToken();
}
@Get('public_url')
getPublicUrl(@Query('url') url: string) {
return this.ossService.getPublicUrl(url);
}
@Get('process_url')
getProcessUrl(@Query('url') url: string, @Query('process') process: string) {
return this.ossService.getProcessUrl(url, process);
}
@Get('upload')
upload() {
return this.ossService.multipartUpload();
}
}
oss.service.ts
import { Injectable, Inject, OnModuleInit, HttpException, HttpStatus } from '@nestjs/common';
import * as OSS from 'ali-oss';
import { STS } from 'ali-oss';
import { Interval } from '@nestjs/schedule';
import { ConfigService } from '@nestjs/config';
import * as path from "path";
type ReturnCredentials = {
accessKeyId: string
accessKeySecret: string,
stsToken: string,
region: string,
bucket: string
}
type OssModuleOptions = {
accessKeyId: string
accessKeySecret: string
roleArn: string
bucket: string
region: string
}
@Injectable()
export class OssService implements OnModuleInit {
@Inject(ConfigService)
private readonly ConfigService: ConfigService
private Client: OSS
private StsClient: STS
private ossCredentials: ReturnCredentials
async onModuleInit() {
const options = this.ConfigService.get<OssModuleOptions>('oss')
this.StsClient = new STS({
accessKeyId: options.accessKeyId,
accessKeySecret: options.accessKeySecret
})
this.Client = new OSS({
region: options.region,
accessKeyId: options.accessKeyId,
accessKeySecret: options.accessKeySecret,
bucket: options.bucket,
})
await this.fetchCredentials()
}
// assumeRole方法阿里有并发限制,因此用定时任务来定时获取
@Interval(1000 * 1400)
private async fetchCredentials() {
const options = this.ConfigService.get<OssModuleOptions>('oss')
// roleArn填写步骤2获取的角色ARN,例如acs:ram::175708322470****:role/ramtest。
// policy填写自定义权限策略,用于进一步限制STS临时访问凭证的权限。如果不指定Policy,则返回的STS临时访问凭证默认拥有指定角色的所有权限。
// 3000为过期时间,单位为秒。
// sessionName用于自定义角色会话名称,用来区分不同的令牌,例如填写为sessiontest。
const result = await this.StsClient.assumeRole(options.roleArn, ``, 3000, 'sessiontest')
this.ossCredentials = {
accessKeyId: result.credentials.AccessKeyId,
accessKeySecret: result.credentials.AccessKeySecret,
stsToken: result.credentials.SecurityToken,
region: options.region,
bucket: options.bucket,
}
}
/**
* 获取临时凭证
*/
async getStsToken() {
if (this.ossCredentials) return this.ossCredentials
await this.fetchCredentials()
return this.ossCredentials
}
/**
* 获取可访问链接
*/
async getPublicUrl(ossPath: string) {
const signUrl = this.Client.signatureUrl(ossPath, {
expires: 24 * 60 * 60,
});
return signUrl
}
/**
* 图片处理
*/
async getProcessUrl(ossPath: string, process: string) {
const signUrl = this.Client.signatureUrl(ossPath, {
process,
expires: 24 * 60 * 60,
});
return signUrl
}
// 图片上传示例
async multipartUpload() {
try {
const result = await this.Client.multipartUpload('exampledir/package.json',
path.normalize(path.join(process.cwd(), 'package.json')),
{});
console.log(result);
} catch (e) {
// 捕获超时异常。
if (e.code === 'ConnectionTimeoutError') {
console.log('TimeoutError');
// do ConnectionTimeoutError operation
}
console.log(e);
throw new HttpException(e.message, HttpStatus.REQUEST_TIMEOUT)
}
}
}
验证
最后我们就演示一个接口获取凭证
在postman调用一下,成功返回客户端所需参数
小结
oss功能非常强大,具体还是需要大家看文档了解。这个也是一般公司用哪个,我们就学哪个