Files
nest-trpc-prisma-starter/apps/server/src/modules/file/file.controller.ts
T
2024-04-26 02:12:44 +08:00

122 lines
3.6 KiB
TypeScript

import path from 'node:path'
import { BadRequestException, Controller, Delete, Get, Param, Post, Query, Req, Res } from '@nestjs/common'
import { ApiTags } from '@nestjs/swagger'
import { Throttle } from '@nestjs/throttler'
import { Bypass } from '@server/common/decorators/bypass.decorator'
import { PagerDto } from '@server/common/dto/pager.dto'
import { BizException } from '@server/common/exceptions/biz.exception'
import { ErrorCodeEnum } from '@server/constants/error-code.constant'
import { alphabet } from '@server/constants/other.constant'
import { FastifyReply, FastifyRequest } from 'fastify'
import { lookup } from 'mime-types'
import { customAlphabet } from 'nanoid'
import { Public } from '../auth/decorators/public.decorator'
import { FileQueryDto, FileUploadDto } from './file.dto'
import { FileService } from './file.service'
@ApiTags('System - 文件模块')
@Controller(['objects', 'files'])
export class FileController {
constructor(
private readonly fileService: FileService,
) { }
@Get('/:type')
async getTypes(@Query() query: PagerDto, @Param() params: FileUploadDto) {
const { type = 'file' } = params
// const { page, size } = query
const dir = await this.fileService.getDir(type)
return Promise.all(
dir.map(async (name) => {
return { name, url: await this.fileService.resolveFileUrl(type, name) }
}),
)
}
@Get('/:type/:name')
@Throttle({ default: { limit: 60, ttl: 60_000 } })
@Bypass()
@Public()
async get(@Param() params: FileQueryDto, @Res() res: FastifyReply) {
const { type, name } = params
const ext = path.extname(name)
const mimetype = lookup(ext)
try {
const stream = await this.fileService.getFileStream(type, name)
if (mimetype) {
res.type(mimetype)
res.header('cache-control', 'public, max-age=31536000')
res.header(
'expires',
new Date(Date.now() + 31536000 * 1000).toUTCString(),
)
}
return res.send(stream)
}
catch {
throw new BizException(ErrorCodeEnum.ResourceNotFound)
}
}
@Post('/upload')
async upload(@Query() query: FileUploadDto, @Req() req: FastifyRequest) {
const file = await req.file()
if (!file)
throw new BadRequestException('仅供上传文件!')
if (file.fieldname !== 'file')
throw new BadRequestException('字段必须为 file')
const { type = 'file' } = query
const ext = path.extname(file.filename)
const filename = customAlphabet(alphabet)(18) + ext.toLowerCase()
if (!(await this.fileService.exists(type, filename)))
await this.fileService.writeFile(type, filename, file.file)
// TODO: save record in dabase
return {
url: await this.fileService.resolveFileUrl(type, filename),
name: filename,
}
}
@Post('/upload/multiple')
async uploadMultiple(@Query() query: FileUploadDto, @Req() req: FastifyRequest) {
const { type = 'file' } = query
const uploadedFiles: any[] = []
const files = req.files()
for await (const file of files) {
const ext = path.extname(file.filename)
const filename = customAlphabet(alphabet)(18) + ext.toLowerCase()
if (!(await this.fileService.exists(type, filename)))
await this.fileService.writeFile(type, filename, file.file)
uploadedFiles.push({
url: await this.fileService.resolveFileUrl(type, filename),
name: filename,
})
}
return uploadedFiles
}
@Delete('/:type/:name')
async delete(@Param() params: FileQueryDto) {
const { type, name } = params
await this.fileService.deleteFile(type, name)
}
}